在Redis中,與Sentinel(哨兵)實現的高可用相比,集群(cluster)更多的是強調數據的分片或者是節點的伸縮性,如果在集群的主節點上加入對應的從節點,集群還可以自動故障轉移,因此相比Sentinel(哨兵)還是有不少優勢的。
以下簡單測試Redis的集群(單機多實例的模式),來體驗一下集群的自動故障轉移功能,同時結合Python,來觀察自動故障轉移過程中應用程序端的表現。
redis集群實例安裝
啟動6個redis集群實例,集群模式,除了正常的配置項目之外,需要在每個主節點中增加集群配置
cluster-enabled yes # 開啟集群模 cluster-node-timeout 1000 # 節點超時時間,單位毫秒,設置一個較小的超時時間,目的是為了后面測試自動故障轉移的效果
分配slot & 主節點握手
主節點分配slot給主節點,三個主節點分配16383個slot
8001主----->8004從
8002主----->8005從
8003主----->8006從
#!/bin/bash
for ((i=0;i<=16383;i++))
do
if [ $i -le 5461 ]; then
/usr/local/redis5_1/bin/redis-cli -h 127.0.0.1 -p 8001 -a root CLUSTER ADDSLOTS $i
elif [ $i -gt 5461 ]&&[ $i -le 10922 ]; then
/usr/local/redis5_1/bin/redis-cli -h 127.0.0.1 -p 8002 -a root CLUSTER ADDSLOTS $i
elif [ $i -gt 10922 ]; then
/usr/local/redis5_1/bin/redis-cli -h 127.0.0.1 -p 8003 -a root CLUSTER ADDSLOTS $i
fi
done
分配完slot之后,在第一個主節點上執行cluster meet 127.0.0.1 8002,cluster meet 127.0.0.1 8003
無須在其他兩個主節點上meet另外兩個主節點,隨后三個主節點之間關系確定會自動確定,目前集群中是三個主節點
添加主節點對應的從節點,需要登錄到每個主節點的實例上,執行
三個從節點分別加入到主節點之后,此時6個節點全部加入到集群中
Python連接至集群測試
這里需要安裝redis-py-cluster依賴包
#!/usr/bin/env python3 import time from time import ctime,sleep from rediscluster import StrictRedisCluster startup_nodes = [ {"host":"111.231.253.***", "port":8001}, {"host":"111.231.253.***", "port":8002}, {"host":"111.231.253.***", "port":8003}, {"host":"111.231.253.***", "port":8004}, {"host":"111.231.253.***", "port":8005}, {"host":"111.231.253.***", "port":8006} ] redis_conn= StrictRedisCluster(startup_nodes=startup_nodes, decode_responses=True,password="root") for i in range(0, 100000): try: redis_conn.set('name' + str(i), str(i)) print('setting name' + str(i) +"--->" + time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))) except: print("connect to redis cluster error") time.sleep(2)
執行上述寫入測試腳本之后,數據基本上均勻地落在三個節點上
自動故障轉移測試
修改Python腳本,每隔1s寫入一條數據,目的是便於觀察在主節點宕機,集群自動故障轉移這個時間段之之內(1s鍾左右),對於應用程序的影響,或者說應用程序在自動故障轉移前后的表現。
如下腳本循環往Redis集群中寫入數據,執行期間,強制殺掉一個主節點,觀察應用程序連接情況。
同時,如果發生異常,暫停應用程序2s,因為上面一開始配置的集群故障轉移時間是1s,如果應用程序暫停2s,完全可以跳過故障轉移的過程,
當故障轉移完成之后,應用程序又恢復成正常狀態,雖然8001節點宕機,應用程序繼續連接8001節點,但是應用程序完全無感知。
import time from time import ctime,sleep from rediscluster import StrictRedisCluster startup_nodes = [ {"host":"111.231.253.***", "port":8001}, {"host":"111.231.253.***", "port":8002}, {"host":"111.231.253.***", "port":8003}, {"host":"111.231.253.***", "port":8004}, {"host":"111.231.253.***", "port":8005}, {"host":"111.231.253.***", "port":8006} ] redis_conn= StrictRedisCluster(startup_nodes=startup_nodes, decode_responses=True,password="root") for i in range(0, 100000): try: redis_conn.set('name' + str(i), str(i)) print('setting name' + str(i) +"--->" + time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))) time.sleep(1) except: print("connect to redis cluster error") time.sleep(2)
發現在殺掉主節點之后,僅發生了一次連接錯誤,隨后因為Redis集群的自動故障轉移成功,對應於程序來說是透明的,因此應用程序隨后正常工作,不受其中一個主節點宕機的影響。
集群此時的狀態,8001節點宕機,明顯,8001對應的從節點8004接管主節點,升級為master,對外提供服務
觀察升級為主節點的8004實例日志,會發現在強制殺掉原8001主節點之后,1秒鍾之內,成功替代8001升級為master節點
如果在故障轉移的過程中,沒有應用程序訪問Redis,應用程序甚至完全不知道Redis集群發生了故障轉移,只要不發生集群中某一個節點的主從節點同時宕機,整個集群就沒有問題,且對應用程序完全透明。
隨后重啟宕機的8001節點,會發現8001節點自動變為其原從節點(8004)的從節點
整體上來看,Redis集群的配置和使用以及自動故障轉移還是比較簡單易容的,這里沒有用redis-trib.rb 而是采用手動分配slot和創建集群的方式,目的是了解完整的配置流程。
需要注意的是:
1,如果開啟了密碼驗證模式,所有的主從節點必須配置masterauth,因為某一個節點宕機重啟之后,會自動變為從節點,此時如果想要從master復制數據,就必須需要主節點的密碼
2,StrictRedisCluster決定了所有主從節點的密碼必須要是一樣的。
表面上看Redis集群簡單易用,自動故障轉移是沒有問題的,保證了高可用,看似沒有問題。
如果細想,這個過程還是有問題的,有沒有發現,雖然故障轉移保證了高可用,但是當從節點升級為主節點之后,如果保證升級為主節點的從節點(8004)一定能夠完全復制原主節點(8001)上的數據?
補充:剛寫完就發現,redis在主從復制上,有一個wait 命令,如下
wait命令可以讓默認的異步復制變為同步復制,
wait numsslave timeout,含義是等待復制到的slave節點的個數和超時時間,如果超時間為0,表示會一直等待(同步到從節點)
如果從節點不可達,則wait會一直阻塞主節點,此時主節點是無法對外提供服務的。
這個就類似於MySQL的半同步復制,主節點上的數據,一定要同步(雖然是relaylog)到從節點,主節點才會提交。
不過回頭想想,取決於如何去對待Redis或者怎么使用Redis,Redis更多的時候是作為一個緩存使用,而不是落地的數據庫,既然是緩存,就應該更多地去考慮性能。