1. 程式人生 > >【Redis】搭建叢集與如何使用Jedis連線叢集

【Redis】搭建叢集與如何使用Jedis連線叢集

1.redis-cluster架構圖

這裡寫圖片描述

架構細節:
(1)所有的redis節點彼此互聯(PING-PONG機制),內部使用二進位制協議優化傳輸速度和頻寬.
(2)節點的fail是通過叢集中超過半數的節點檢測失效時才生效.
(3)客戶端與redis節點直連,不需要中間proxy層.客戶端不需要連線叢集所有節點,連線叢集中任何一個可用節點即可
(4)redis-cluster把所有的物理節點對映到[0-16383]slot上,cluster 負責維護node<->slot<->value

Redis 叢集中內建了 16384 個雜湊槽,當需要在 Redis 叢集中放置一個 key-value 時,redis 先對 key 使用 crc16 演算法算出一個結果,然後把結果對 16384 求餘數,這樣每個 key 都會對應一個編號在 0-16383 之間的雜湊槽,redis 會根據節點數量大致均等的將雜湊槽對映到不同的節點

示例如下:
這裡寫圖片描述

2.redis-cluster投票:容錯

這裡寫圖片描述

(1)叢集中所有master參與投票,如果半數以上master節點與其中一個master節點通訊超過(cluster-node-timeout),認為該master節點掛掉.
(2):什麼時候整個叢集不可用(cluster_state:fail)
如果叢集任意master掛掉,且當前master沒有slave,則叢集進入fail狀態。也可以理解成叢集的[0-16383]slot對映不完全時進入fail狀態。如果叢集超過半數以上master掛掉,無論是否有slave,叢集進入fail狀態。

3.搭建Ruby環境

redis叢集管理工具redis-trib.rb依賴ruby環境,首先需要安裝ruby環境。
1
)安裝ruby [root@localhost bin]#yum install ruby [root@localhost bin]#yum install rubygems 2)使用sftp工具上傳redis-4.0.0.gem至/java下 3)安裝ruby和redis的介面程式 [root@localhost java]#gem install redis-4.0.0.gem 4)將Redis叢集搭建指令碼檔案複製到/opt/java/redis-cluster目錄下 [root@localhost /]# cd /java/redis-4.0.0/src/ [root@localhost
src]# ll *.rb -rwxrwxr-x. 1 root root 48141 41 2015 redis-trib.rb [root@localhost src]# cp redis-trib.rb /opt/java/redis-cluster/ -r

4.叢集的搭建過程

搭建叢集最少也得需要3臺主機,如果每臺主機再配置一臺從機的話,則最少需要6臺機器。
1)建立6個redis例項,需要埠號7001~7006,複製出一個7001機器
[root@localhost redis]# cp bin ../redis-cluster/7001 -r
2)如果存在持久化檔案,則刪除
[root@localhost 7001]# rm -rf appendonly.aof  dump.rdb
3)設定叢集引數和修改埠

這裡寫圖片描述

這裡寫圖片描述

4)複製出7002-7006機器,然後修改7002-7006機器的埠
[root@localhost redis-cluster]# cp 7001/ 7002 -r
[root@localhost redis-cluster]# cp 7001/ 7003 -r
[root@localhost redis-cluster]# cp 7001/ 7004 -r
[root@localhost redis-cluster]# cp 7001/ 7005 -r
[root@localhost redis-cluster]# cp 7001/ 7006 -r
5)啟動7001-7006這六臺機器
[root@localhost redis-cluster]# vi start.sh
cd 7001
./redis-server redis.conf
cd ..
cd 7002
./redis-server redis.conf
cd ..
cd 7003
./redis-server redis.conf
cd ..
cd 7004
./redis-server redis.conf
cd ..
cd 7005
./redis-server redis.conf
cd ..
cd 7006
./redis-server redis.conf
cd ..
[root@localhost redis-cluster]# chmod 777 start.sh 
[root@localhost redis-cluster]# ./start.sh 
[root@localhost redis-cluster]# ps -ef|grep redis
root       3750   3677  0 20:52 pts/2    00:00:00 cp -i redis-trib.rb /opt/java/redis-cluster
root       3779   3677  0 20:54 pts/2    00:00:00 cp -i redis-trib.rb /opt/java/redis-cluster -r
root       3791      1  1 20:55 ?        00:00:00 ./redis-server *:7001 [cluster]
root       3795      1  0 20:55 ?        00:00:00 ./redis-server *:7002 [cluster]
root       3799      1  0 20:55 ?        00:00:00 ./redis-server *:7003 [cluster]
root       3803      1  0 20:55 ?        00:00:00 ./redis-server *:7004 [cluster]
root       3807      1  1 20:55 ?        00:00:00 ./redis-server *:7005 [cluster]
root       3811      1  0 20:55 ?        00:00:00 ./redis-server *:7006 [cluster]
root       3815   3444  0 20:55 pts/1    00:00:00 grep --color=auto redis
6)建立叢集
[root@localhost redis-cluster]# vi startCluster.sh 
./redis-trib.rb create --replicas 1 10.39.12.237:7001 10.39.12.237:7002 10.39.12.237:7003 10.39.12.237:7004 10.39.12.237:7005 10.39.12.237:7006
[root@localhost redis-cluster]# chmod 777 startCluster.sh 
[root@localhost redis-cluster]# ./startCluster.sh 
>>> Creating cluster
Connecting to node 10.39.12.237:7001: OK
>>> Performing hash slots allocation on 6 nodes...
Using 6 masters:
10.39.12.237:7001
10.39.12.237:7002
10.39.12.237:7003
10.39.12.237:7004
10.39.12.237:7005
10.39.12.237:7006
..............
..............
Can I set the above configuration? (type 'yes' to accept): yes
.............
.............
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

5.連線叢集

-c:指定是叢集連線
[[email protected] 7001]# ./redis-cli -h 192.168.1.110 -p 7001 -c
192.168.1.110:7001> set test hello
-> Redirected to slot [6918] located at 10.39.12.237:7003
OK
10.39.12.237:7003> get test
"hello"
10.39.12.237:7003> 

6.檢視叢集資訊

10.39.12.237:7003> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:6
cluster_current_epoch:6
cluster_my_epoch:3
cluster_stats_messages_sent:5335
cluster_stats_messages_received:5335

7.檢視叢集節點

10.39.12.237:7003> cluster nodes
534dd9ab4c373f3a145c1f5838003e141f46a608 10.39.12.237:7005 slave  - 0 1513258673541 5 connected 10923-13652
4a8a2716ed36415bd82b91dc3662ca56d999bced 10.39.12.237:7001 master - 0 1513258676590 1 connected 0-2730
f22201dcb909630c98105099311aa1742280237e 10.39.12.237:7003 myself,master - 0 0 3 connected 5461-8191
a090f5b3e28a5ce7b237f2872f40a39f0213c5d7 10.39.12.237:7002 master - 0 1513258677607 2 connected 2731-5460
9f2df6a9df345ab2314fc5a109c70a6e031e7297 10.39.12.237:7004 slave  - 0 1513258678623 4 connected 8192-10922
310cb997e025ac596fb690413f7d06acc8f671e3 10.39.12.237:7006 slave  - 0 1513258679640 6 connected 13653-16383

8.維護節點

1)新增主節點
叢集建立成功後可以向叢集中新增節點,下面是新增一個master主節點,新增7007結點作為新節點。
[root@localhost redis-cluster]# ./redis-trib.rb add-node 10.39.12.237:7007 10.39.12.237:7001
2)hash槽重新分配
新增完主節點需要對主節點進行hash槽分配,這樣該主節才可以儲存資料。檢視叢集中槽佔用情況,redis叢集有16384個槽,叢集中的每個結點分配自已槽,通過檢視叢集結點可以看到槽佔用情況。
127.0.0.1:7001> cluster nodes

給剛新增的7007結點分配槽
第一步:連線上叢集(連線叢集中任意一個可用結點都行)
[root@localhost redis-cluster]# ./redis-trib.rb  reshard 10.39.12.237:7001
第二步:輸入要分配的槽數量

這裡寫圖片描述

輸入:100,表示要分配100個槽
第三步:輸入接收槽的結點id

這裡寫圖片描述

輸入:e4ccb3217026fcff20f5d4110883a5ac2d614b6d
PS:這裡準備給7007分配槽,通過cluster nodes檢視7007結點id為:
e4ccb3217026fcff20f5d4110883a5ac2d614b6d
第四步:輸入源結點id

這裡寫圖片描述

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

這裡寫圖片描述

3)新增從節點
叢集建立成功後可以向叢集中新增節點,下面是新增一個slave從節點,新增7008從結點,將7008作為7007的從結點。
命令:./redis-trib.rb add-node --slave --master-id 主節點id 新節點的ip和埠 舊節點ip和埠
[[email protected] redis-cluster]# ./redis-trib.rb add-node --slave --master-id 36c36b350c8bb38dcdded0daa21151b0eaae38d5 10.39.12.237:7008 10.39.12.237:7007

注意:如果原來該結點在叢集中的配置資訊已經生成到cluster-config-file指定的配置檔案中(如果cluster-config-file沒有指定則預設為nodes.conf),這時可能會報錯:
[ERR] Node 10.39.12.237:7008 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指令

4)刪除結點
[[email protected] redis-cluster]#./redis-trib.rb del-node 10.39.12.237:7002 4b45eb75c8b428fbd77ab979b85080146a9bc017
刪除已經佔有hash槽的結點會失敗,報錯如下:
[ERR] Node 10.39.12.237:7002 is not empty! Reshard data away and try again.需要將該結點佔用的hash槽分配出去

9.Jedis連線叢集

1)防火牆配置
[root@localhost /]# vim /etc/sysconfig/iptables

這裡寫圖片描述

2)程式碼實現
->1.建立JedisCluster類連線redis叢集。
    @Test
    public void jedisCluster(){
        Set<HostAndPort> nodes = new HashSet<>();
        nodes.add(new HostAndPort("10.39.12.237", 7001));
        nodes.add(new HostAndPort("10.39.12.237", 7002));
        nodes.add(new HostAndPort("10.39.12.237", 7003));
        nodes.add(new HostAndPort("10.39.12.237", 7004));
        nodes.add(new HostAndPort("10.39.12.237", 7005));
        nodes.add(new HostAndPort("10.39.12.237", 7006));
        //JedisCluster物件,在系統中是單例存在
        JedisCluster jedisCluster = new JedisCluster(nodes);
        jedisCluster.set("s8", "hello world");
        String result = jedisCluster.get("s8");
        System.out.println(result);
        jedisCluster.close();
    }
->2.使用spring
配置applicationContext.xml
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
    <constructor-arg index="0">
        <set>
            <bean class="redis.clients.jedis.HostAndPort">
                <constructor-arg index="0" value="10.39.12.237"></constructor-arg>
                <constructor-arg index="1" value="7001"></constructor-arg>
            </bean>
            <bean class="redis.clients.jedis.HostAndPort">
                <constructor-arg index="0" value="10.39.12.237"></constructor-arg>
                <constructor-arg index="1" value="7002"></constructor-arg>
            </bean>
            <bean class="redis.clients.jedis.HostAndPort">
                <constructor-arg index="0" value="10.39.12.237"></constructor-arg>
                <constructor-arg index="1" value="7003"></constructor-arg>
            </bean>
            <bean class="redis.clients.jedis.HostAndPort">
                <constructor-arg index="0" value="10.39.12.237"></constructor-arg>
                <constructor-arg index="1" value="7004"></constructor-arg>
            </bean>
            <bean class="redis.clients.jedis.HostAndPort">
                <constructor-arg index="0" value="10.39.12.237"></constructor-arg>
                <constructor-arg index="1" value="7005"></constructor-arg>
            </bean>
            <bean class="redis.clients.jedis.HostAndPort">
                <constructor-arg index="0" value="10.39.12.237"></constructor-arg>
                <constructor-arg index="1" value="7006"></constructor-arg>
            </bean>
        </set>
    </constructor-arg>
    <constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg> 
</bean>

測試程式碼:
    @Test
    public void jedisClusterSpring(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/config/applicationContext.xml");
        JedisCluster jedis = (JedisCluster) applicationContext.getBean("jedisCluster");
        jedis.set("s9", "welcome");
        String result = jedis.get("s9");
        System.out.println(result);
    }