Redis分佈式集羣搭建

Redis集羣架構圖

 

 

上圖藍色爲redis集羣的節點。node

節點之間經過ping命令來測試鏈接是否正常,節點之間沒有主區分,鏈接到任何一個節點進行操做時,均可能會轉發到其餘節點。redis

一、Redis的容錯機制

節點之間會定時的互相發送ping命令,測試節點的健康狀態,當節點接受到ping命令後,會返回一個pong字符串。算法

投票機制:若是一個節點A給節點B發送ping沒有獲得pong返回,會通知其餘節點再次給B發送ping,若是集羣中有超過一半的節點收不B節點的pong。那麼就認爲B節點掛了。通常會爲每一個節點提供一個備份節點,若是掛掉會切換到備份節點。spring

二、Redis集羣存儲原理

Redis對於每一個存放的key會進行hash操做,生成一個[0-16384]hash值(先進行ruby

crc 算法再對16384取餘)。服務器

集羣的狀況下,就是把[0-16384]的區間進行拆分,放到不一樣的redis中。架構

 

三、Redis的持久化

Snapshotting:定時的將Redis內存中的數據保存到硬盤中app

AOF:將全部的command操做保存到aof中,AOP的同步頻率很高,數據即便丟失,粒度也很小,但會在性能上形成影響。工具

 

2、Redis集羣準備工做

Redis安裝

源碼下載性能

下載地址https://pan.baidu.com/s/1bCcLv4  密碼i5k6

解壓源碼

   tar -zxvf redis-3.0.0.tar.gz  

進入解壓後的目錄進行編譯

cd /usr/local/redis-3.0.0

make

安裝到指定目錄,/usr/local/redis

cd /usr/local/redis-3.0.0

make PREFIX=/usr/local/redis install

 

nredis.conf

redis.confredis的配置文件,redis.confredis源碼目錄。

注意修改port做爲redis進程的端口,port默認6379

 

拷貝配置文件到安裝目錄下

進入源碼目錄,裏面有一份配置文件 redis.conf,而後將其拷貝到安裝路徑下

cd /usr/local/redis

mkdir conf

cp /usr/local/redis-3.0.0/redis.conf  /usr/local/redis/bin

運行:bin/redis-server  將出現下圖畫面:

 

 

Redis默認是前臺運行的,能夠修改redis.confdaemonize yes ,將其變成後臺運行。

 

集羣環境搭建

redis集羣管理工具redis-trib.rb依賴ruby環境,首先須要安裝ruby環境

安裝ruby

yum install ruby

yum install rubygems

安裝rubyredis的接口程序

拷貝redis-3.0.0.gem/usr/local

執行:

gem install /usr/local/redis-3.0.0.gem

 

3、建立Redis集羣

在一臺服務器上,能夠用不一樣端口號來表示不一樣redis服務器。

Redis集羣最少須要三臺服務器,而每臺服務器有須要備用服務器,因此最少須要6臺服務器。端口規劃以下:

主服務器:192.168.100.66 :7001  :7002  :7003

從服務器:192.168.100.66 :7004  :7005  :7006

/usr/local 建立文件夾用來存放服務器程序

mkdir 7001 7002 7003 7004 7005 7006

若是想讓redis支持集羣須要修改redis.config配置文件的cluster-enabled yes

本例中咱們以端口來區別不一樣的redis服務,因此還須要修改redis.configport爲對應端口

修改完配置文件,將redis安裝目錄的bin複製到上面每一個目錄中。

分別進入7001/bin/ 7002/bin .....

啓動服務./redis-server ./redis.conf

 

查看redis進程:ps -aux|grep redis 以下圖則說明啓動成功

 

建立集羣:

將以前解壓的文件夾的redis-3.0.0/src/redis-trib.rb複製到redis-cluster目錄

運行

./redis-trib.rb create --replicas 1 192.168.100.66:7001 192.168.100.66:7002 192.168.100.66:7003 192.168.100.66:7004 192.168.100.66:7005  192.168.100.66:7006

 

若是執行時報以下錯誤:

[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0

解決方法是刪除生成的配置文件nodes.conf,若是不行則說明如今建立的結點包括了舊集羣的結點信息,須要刪除redis的持久化文件後再重啓redis,好比:appendonly.aofdump.rdb

若是成功最終輸入以下:

 

查詢集羣信息:

說明:

./redis-cli -c -h 192.168.101.3 -p 7001 ,其中-c表示以集羣方式鏈接redis-h指定ip地址,-p指定端口號

cluster nodes 查詢集羣結點信息

cluster info 查詢集羣狀態信息

 

hash槽從新分配

第一步:鏈接上集羣

./redis-trib.rb reshard 192.168.101.3:7001(鏈接集羣中任意一個可用結點都行)

 

第二步:輸入要分配的槽數量

 

輸入 500表示要分配500個槽

 

第三步:輸入接收槽的結點id

 

 

這裏準備給7007分配槽,經過cluster nodes查看7007結點id15b809eadae88955e36bcdbb8144f61bbbaf38fb

輸入:15b809eadae88955e36bcdbb8144f61bbbaf38fb

 

第四步:輸入源結點id

 

這裏輸入all

第五步:輸入yes開始移動槽到目標結點id

 

添加從節點

 

集羣建立成功後能夠向集羣中添加節點,下面是添加一個slave從節點。

添加7008從結點,將7008做爲7007的從結點。

 

./redis-trib.rb add-node --slave --master-id 主節點id 添加節點的ip和端口 集羣中已存在節點ip和端口

 

 

執行以下命令:

./redis-trib.rb add-node --slave --master-id cad9f7413ec6842c971dbcc2c48b4ca959eb5db4  192.168.101.3:7008 192.168.101.3:7001

cad9f7413ec6842c971dbcc2c48b4ca959eb5db4  7007結點的id,可經過cluster nodes查看。

 

注意:若是原來該結點在集羣中的配置信息已經生成cluster-config-file指定的配置文件中(若是cluster-config-file沒有指定則默認爲nodes.conf),這時可能會報錯:

[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0

解決方法是刪除生成的配置文件nodes.conf,刪除後再執行./redis-trib.rb add-node指令

查看集羣中的結點,剛添加的70087007的從節點:

 

1.1. 刪除結點:

 

./redis-trib.rb del-node 127.0.0.1:7005 4b45eb75c8b428fbd77ab979b85080146a9bc017

 

刪除已經佔有hash槽的結點會失敗,報錯以下:

[ERR] Node 127.0.0.1:7005 is not empty! Reshard data away and try again.

 

須要將該結點佔用的hash槽分配出去(參考hash槽從新分配章節)。

測試:

Maven:

<dependencies>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.7.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>4.3.10.RELEASE</version>
        <scope>test</scope>
    </dependency>
</dependencies>

 

 

普通測試:

@Test
public void redisClusterTest1(){
    JedisPoolConfig config=new JedisPoolConfig();
    config.setMaxTotal(30);
    config.setMaxIdle(2);

    Set<HostAndPort> jedisNode=new HashSet<HostAndPort>();
    jedisNode.add(new HostAndPort("192.168.100.66",7001));
    jedisNode.add(new HostAndPort("192.168.100.66",7002));
    jedisNode.add(new HostAndPort("192.168.100.66",7003));
    jedisNode.add(new HostAndPort("192.168.100.66",7004));
    jedisNode.add(new HostAndPort("192.168.100.66",7005));
    jedisNode.add(new HostAndPort("192.168.100.66",7006));

    JedisCluster jc=new JedisCluster(jedisNode,config);
    jc.set("name","老王");
    String value=jc.get("name");
    System.out.println(value);
}

Spring測試:

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 鏈接池配置 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 最大鏈接數 -->
        <property name="maxTotal" value="30" />
        <!-- 最大空閒鏈接數 -->
        <property name="maxIdle" value="10" />
        <!-- 每次釋放鏈接的最大數目 -->
        <property name="numTestsPerEvictionRun" value="1024" />
        <!-- 釋放鏈接的掃描間隔(毫秒) -->
        <property name="timeBetweenEvictionRunsMillis" value="30000" />
        <!-- 鏈接最小空閒時間 -->
        <property name="minEvictableIdleTimeMillis" value="1800000" />
        <!-- 鏈接空閒多久後釋放, 當空閒時間>該值 且 空閒鏈接>最大空閒鏈接數 時直接釋放 -->
        <property name="softMinEvictableIdleTimeMillis" value="10000" />
        <!-- 獲取鏈接時的最大等待毫秒數,小於零:阻塞不肯定的時間,默認-1 -->
        <property name="maxWaitMillis" value="1500" />
        <!-- 在獲取鏈接的時候檢查有效性, 默認false -->
        <property name="testOnBorrow" value="true" />
        <!-- 在空閒時檢查有效性, 默認false -->
        <property name="testWhileIdle" value="true" />
        <!-- 鏈接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true -->
        <property name="blockWhenExhausted" value="false" />
    </bean>
    <!-- redis集羣 -->
    <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
        <constructor-arg index="0">
            <set>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
                    <constructor-arg index="1" value="7001"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
                    <constructor-arg index="1" value="7002"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
                    <constructor-arg index="1" value="7003"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
                    <constructor-arg index="1" value="7004"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
                    <constructor-arg index="1" value="7005"></constructor-arg>
                </bean>
                <bean class="redis.clients.jedis.HostAndPort">
                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>
                    <constructor-arg index="1" value="7006"></constructor-arg>
                </bean>
            </set>
        </constructor-arg>
        <constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg>
    </bean>
</beans>

 

測試類:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring-config.xml"})
public class RedisClusterTest {
    @Autowired
    private JedisCluster jedisCluster;
    @Test
    public void redisClusterTest2(){
        jedisCluster.set("username","小明啦啦");
        String name=jedisCluster.get("username");
        System.out.println(name);    }}

相關文章
相關標籤/搜索