6.redis sentinel實現高可用讀寫分離
1. redis sentinel
故障轉移的基本原理:
- 多個sentinel發現並確認master有問題
- 選舉出一個sentinel作為領導
- 選出一個slave稱為新的master的slave
- 通知其他的slave稱為新的master的slave
- 通知客戶端主從變化
- 等待老的master復活稱為新的master的slave
也支援多個master-slave結構:
2. 安裝與配置
- 配置開啟主從節點
- 配置開啟sentinel監控主節點(sentinel是特殊的redis)
- 實際應該多臺機器,但是演示方便,只用一臺機器來搭建
- 詳細配置節點
本地安裝的結構圖:
對於master:redis-7000.conf配置:
port 7000
daemonize yes
pidfile /usr/local/redis/data/redis-7000.pid
logfile "7000.log"
dir "/usr/local/redis/data"
對於slave:redis-7001和redis-7002配置:
port 7001
daemonize yes
pidfile /usr/local/redis/data/redis-7001.pid
logfile "7001.log"
dir "/usr/local/redis/data"
slaveof 127.0.0.1 7000
啟動redis服務:
redis-server ../config/redis-7000.conf
訪問7000埠的master redis:
redis-cli -p 7000 info replication
顯示他有兩個從節點:
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=7002,state=online,offset=99550,lag=1
slave1:ip=127.0.0.1,port=7001,state=online,offset=99816,lag=0
master_repl_offset:99816
repl_backlog_active:1
repl_backlog_size: 1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:99815
對於sentinel主要配置:
master sentinel config:
port 26379
daemonize yes
dir "/usr/local/redis/data"
logfile "26379.log"
sentinel monitor mymaster 127.0.0.1 7000 2
...
啟動redis sentinel:
redis-sentinel ../config/redis-sentinel-26379.conf
訪問26379 redis sentinel master:
redis-cli -p 26379 info sentinel
顯示:
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=3
檢視這六個程序是否都起來了:ps -ef | grep redis
注意,如果上面是配置在虛擬機器的話,需要將127.0.0.1改為虛擬機器的ip,要不然找不著。
3. 故障轉移演練
3.1 java客戶端程式
JedisSentinelPool只是一個配置中心,不需要具體連線某個redis,注意它不是代理。
private Logger logger = LoggerFactory.getLogger(AppTest.class);
@Test
public void test4(){
//哨兵配置,我們訪問redis,就通過sentinel來訪問
String masername = "mymaster";
Set<String> sentinels = new HashSet<>();
sentinels.add("10.128.24.176:26379");
sentinels.add("10.128.24.176:26380");
sentinels.add("10.128.24.176:26381");
JedisSentinelPool sentinelPool = new JedisSentinelPool(masername,sentinels);
//一個while死迴圈,每隔一秒往master塞入一個值,並且日誌列印
while (true){
Jedis jedis = null;
try{
jedis = sentinelPool.getResource();
int index = new Random().nextInt(100000);
String key = "k-" + index;
String value = "v-" + index;
jedis.set(key,value);
logger.info("{} value is {}",key,jedis.get(key));
TimeUnit.MILLISECONDS.sleep(1000);
}catch (Exception e){
logger.error(e.getMessage(),e);
}finally {
if(jedis != null){
jedis.close();
}
}
}
}
maven依賴是:
<!--jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!--slf4j日誌介面-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.6</version>
</dependency>
<!--logback日誌實現-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.1</version>
</dependency>
啟動程式,發現是正常寫入:
16:16:01.424 [main] INFO com.njupt.swg.AppTest - k-54795 value is v-54795
16:16:02.426 [main] INFO com.njupt.swg.AppTest - k-55630 value is v-55630
16:16:03.429 [main] INFO com.njupt.swg.AppTest - k-70642 value is v-70642
16:16:04.430 [main] INFO com.njupt.swg.AppTest - k-42978 value is v-42978
16:16:05.431 [main] INFO com.njupt.swg.AppTest - k-96297 value is v-96297
16:16:06.433 [main] INFO com.njupt.swg.AppTest - k-4220 value is v-4220
16:16:07.435 [main] INFO com.njupt.swg.AppTest - k-34103 value is v-34103
16:16:08.436 [main] INFO com.njupt.swg.AppTest - k-9177 value is v-9177
16:16:09.437 [main] INFO com.njupt.swg.AppTest - k-24389 value is v-24389
16:16:10.439 [main] INFO com.njupt.swg.AppTest - k-32325 value is v-32325
16:16:11.440 [main] INFO com.njupt.swg.AppTest - k-68538 value is v-68538
16:16:12.441 [main] INFO com.njupt.swg.AppTest - k-36233 value is v-36233
16:16:13.443 [main] INFO com.njupt.swg.AppTest - k-305 value is v-305
16:16:14.444 [main] INFO com.njupt.swg.AppTest - k-59279 value is v-59279
我們將現在的埠為7000的redis master 給kill掉
kill -9 master的pid
我們會發現:客戶端報異常,但是在大概十幾秒之後,就繼續正常塞值了。原因是服務端的哨兵機制的選舉matser需要一定的時間。
4. 三個定時任務
4.1 每10秒每個sentinel對master和slave執行Info
- 發現slave節點
- 確認主從關係
4.2 每2秒每個sentinel通過master節點的channel交換資訊(pub/sub)
- 通過sentinel:hello進行頻道互動
- 互動對節點的“看法”和自身資訊
4.3 每1秒每個sentinel對其他sentinel和redis執行ping
- 心跳監測,失敗判定依據
5. 主觀下線和客觀下線
對於之前的Sentinel配置檔案中有兩條配置:
監控master redis節點,這裡是當超過兩個sentinel認為master掛了,則認為master掛了。
sentinel monitor
sentinel monitor mymaster 127.0.0.1 6379 2
這裡是每秒sentinel都回去Ping周圍的master redis,超過30秒沒有任何相應,說明其掛了。
sentinel down-after-milliseconds
sentinel down-after-milliseconds mymaster 300000
5.1 主觀下線
主觀下線:每個sentinel節點對Redis節點失敗的“偏見”
這是一種主觀下線。因為在複雜的網路環境下,這個sentinel與這個master不通,但是master與其他的sentinel都是通的呢?所以是一種“偏見”
這是依靠的第三種定時:每秒去ping一下週圍的sentinel和redis。對於slave redis,可以使用這個主觀下線,因為他不需要進行故障轉移。
5.2 客觀下線
客觀下線:所有sentinel節點對master Redis節點失敗“達成共識”(超過quorum個則統一)
這是依靠的第二種定時:每兩秒,sentinel之間進行“商量”,傳遞的訊息是:sentinel is-master-down-by-addr
對於master redis的下線,必須要達成共識才可以,因為涉及故障轉移,僅僅依靠一個sentinel判斷是不夠的。
6. 領導者選舉
原因:只有一個sentinel節點完成故障轉移
選舉:通過sentinel is-master-down-by-addr命令都希望成為領導者
- 每個做主觀下線的sentinel節點向其他sentinel節點發送命令,要求將它設定為領導者
- 收到命令的sentinel節點如果沒有同意通過其他semtinel節點發送的命令,那麼將同意該請求,否則拒絕
- 如果該sentinel節點發現自己的票數已經超過sentinel集合半數並且超過quorum,那麼它將成為領導者。
- 如果此過程中多個sentinel節點成為了領導者,那麼將等待一段時間重新進行選舉
7. 故障轉移
- 從slave節點中選出一個“合適的”節點作為新的master節點
- 對上述的slave節點執行“slaveof no one”命令使其成為master節點
- 向剩餘的slave節點發送命令,讓它們成為新master節點的slave節點,複製規則和parallel-syncs引數一樣
- 更新對原來的master節點配置為slave,並保持著對其“關注”,當恢復後命令他去複製新的master節點
那麼,如何選擇“合適”的slave節點呢?
- 選擇slave-priority(slave節點優先順序)最高的slave節點,如果存在則返回,不存在則繼續。
- 選擇複製偏移量對打的slave節點(複製得最完整),如果存在則返回,不存在則繼續
- 選擇run_id最小的slave節點(最早的節點)
8. 節點下線
主節點下線:sentinel failover
從節點下線要注意讀寫分離問題。
9. 總結與思考
redis sentinel是redis高可用實現方案:故障發現、故障自動轉移、配置中心、客戶端通知。
redis sentinel從redis2.8版本才正式生產可用,之前版本不可生產用。
儘可能在不同物理機上部署redis sentinel所有節點。
redis sentinel中的sentinel節點個數應該大於等於3且最好是奇數。
redis sentinel中的資料節點和普通資料節點沒有區別。每個sentinel節點在本質上還是一個redis例項,只不過和redis資料節點不同的是,其主要作用是監控redis資料節點
客戶端初始化時連線的是sentinel節點集合,不再是具體的redis節點,但sentinel只是配置中心不是代理。
redis sentinel通過三個定時任務實現了sentinel節點對於主節點、從節點、其餘sentinel節點的監控。
redis sentinel在對節點做失敗判定時分為主觀下線和客觀下線。
看懂redis sentinel故障轉移日誌對於redis sentinel以及問題排查非常有用。
redis sentinel實現讀寫分離高可用可以依賴sentinel節點的訊息通知,獲取redis資料節點的狀態變化。
redis sentinel可以實現高可用的讀寫分離,高可用體現在故障轉移,那麼實現高可用的基礎就是要有從節點,主從節點還實現了讀寫分離,減少master的壓力。但是如果是從節點下線了,sentinel是不會對其進行故障轉移的,並且連線從節點的客戶端也無法獲取到新的可用從節點,而這些問題在Cluster中都得到了有效的解決。
對於效能提高、容量擴充套件的時候,這種方式是比較複雜的,比較推薦的是使用叢集,就是下面討論的redis cluster!