1. 高可用性
redis sentinel 官方文檔
redis sentinel 中文文檔
前面說到使用主從備份的方式可以保證數(shù)據(jù)的安全性,當(dāng)主節(jié)點(diǎn)掛了的時候,從節(jié)點(diǎn)可以頂上作為主節(jié)點(diǎn)。但是需要手動去切換,這個過程需要認(rèn)為干預(yù)。那么有沒有一種由程序自己監(jiān)控主節(jié)點(diǎn)狀態(tài)并在主節(jié)點(diǎn)掛了的時候自己選擇一個從節(jié)點(diǎn)的方案呢?答案肯定是有的。redis sentinel(哨兵)。
從宏觀上講,redis sentinel 可以做到的如下幾點(diǎn):
- Monitoring 監(jiān)控,可以監(jiān)控master和slaves實(shí)例是不是按照預(yù)期的執(zhí)行
- Notification 通知,當(dāng)被監(jiān)控的redis的實(shí)例出現(xiàn)問題。能夠通知系統(tǒng)管理員或者其他應(yīng)用程序
- Automatic failover 自動災(zāi)難遷移,當(dāng)master出現(xiàn)故障的時候,sentinel會開啟一個選舉流程,選擇一個合適slave升級為master
- Configuration provider 幫客戶端發(fā)現(xiàn)redis服務(wù)??蛻舳诉B接sentinel,sentinel拿到當(dāng)前redis master address 給客戶端提供服務(wù)。當(dāng)redis master 掛了的時候,找到一個新的service address。
sentinel具有分布式的特性,他被設(shè)計成多個Sentinel 進(jìn)行一起協(xié)同工作。這樣左右兩個有點(diǎn):
- 故障檢測的時候,多個進(jìn)程認(rèn)為出現(xiàn)故障的時候才認(rèn)為出現(xiàn)故障。避免wupan
- 多個sentinel進(jìn)程一起工作,當(dāng)有部分sentinel死掉的時候,系統(tǒng)仍然能夠繼續(xù)工作。sentinel不一定要求所有的sentinel一起工作
2. 啟動和配置
在主從配置的時候把slave配置bind ,因為他有可能成為master。
sentinel服務(wù)是和redis服務(wù)一起發(fā)布。啟動腳本位置和redis的啟動位置一樣 redis-sentinel
腳本就是的。啟動方式如下:
## pwd=redis-3.2.8
## 方式一
src/redis-sentinel sentinel.conf
## 方式二
src/redis-server sentinel.conf --sentinel
兩種啟動方式是一摸一樣的。redis服務(wù)和sentinel一起使用的使用的時候,redis啟動和sentinel啟動都需要指明配置文件。因為在sentinel進(jìn)行故障遷移切換master的時候會修改原來的master和slave的redis和sentinel的配置。
sentinel的配置,redis提供個默認(rèn)的配置sentinel.conf。 配置項如下:
protected-mode no # 設(shè)置成no,沒有訪問限制
port 5000
sentinel monitor mymaster 192.168.206.200 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
官方說法是,使用sentinel的時候,最好3個以上的sentinel實(shí)例(instance)較好,因為他的選舉策略要投票過半。
上面的配置,每個實(shí)例使用相同的配置即可,如果sentinel有在同一個機(jī)器上,需要修改下端口保持不同,避免端口占用的問題
現(xiàn)在來看這個配置各項說明
- sentinel monitor mymaster 192.168.206.200 6379 2 他的配置 規(guī)則是
sentinel monitor <master-group-name> <ip> <port> <quorum>
master-group-name: master的名稱,唯一標(biāo)志,ip, port redis服務(wù) master的ip和端口
quorum: 要確定master掛了,知道要多少個sentinel實(shí)例投票
- 選項配置
sentinel <option_name> <master_name> <option_value>
down-after-milliseconds:在master沒有回應(yīng)后多久(多少毫秒)認(rèn)為master掛掉
failover-timeout:故障遷移進(jìn)程多久超時,設(shè)置時間內(nèi)沒有完成認(rèn)為超時
parallel-syncs:故障遷移的時候,最多同時幾臺和master數(shù)據(jù)同步。數(shù)字越小,故障遷移越耗時,但是如果slave配置了master遷移繼續(xù)使用old data提供服務(wù)的話。建議配置此項為1。
3. 部署架構(gòu)
在redis得官方文檔中,建議不用使用只有兩個sentinel的實(shí)例的方式部署,可用不高,建議知道要3個實(shí)例。示例的部署方案如下:
示例中 M1..Mn代表master,R1...R2代表slave , S1....Sn代表sentinel實(shí)例,C1.....Cn clients
- redis實(shí)力和sentinel在同一臺機(jī)器,1master ,2slave模式
+----+
| M1 |
| S1 |
+----+
|
+----+ | +----+
| R2 |----+---- | R3 |
| S2 | | S3 |
+----+ +----+
Configuration: quorum = 2
- 1 master,1 slave, sentinel放在客戶端這邊
+----+ +----+
| M1 |----+----| R1 |
| | | | |
+----+ | +----+
|
+------------+------------+
| | |
| | |
+----+ +----+ +----+
| C1 | | C2 | | C3 |
| S1 | | S2 | | S3 |
+----+ +----+ +----+
Configuration: quorum = 2
- 1 master ,1 slave
+----+ +----+
| M1 |----+----| R1 |
| S1 | | | S2 |
+----+ | +----+
|
+------+-----+
| |
| |
+----+ +----+
| C1 | | C2 |
| S3 | | S4 |
+----+ +----+
Configuration: quorum = 3
4. 高級概念
大部分概念在文章開頭提到的中英文文檔中都有介紹。這里簡單說下幾個最基本的
4.1. 主觀下線和客觀下線
- 主觀下線(Subjectively Down, 簡稱 SDOWN)指的是單個 Sentinel 實(shí)例對服務(wù)器做出的下線判斷。
- 客觀下線(Objectively Down, 簡稱 ODOWN)指的是多個 Sentinel 實(shí)例在對同一個服務(wù)器做出 SDOWN 判斷, 并且通過 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服務(wù)器下線判斷。 (一個 Sentinel 可以通過向另一個 Sentinel 發(fā)送 SENTINEL is-master-down-by-addr 命令來詢問對方是否認(rèn)為給定的服務(wù)器已下線。)
如果一個服務(wù)器沒有在 master-down-after-milliseconds 選項所指定的時間內(nèi), 對向它發(fā)送 PING 命令的 Sentinel 返回一個有效回復(fù)(valid reply), 那么 Sentinel 就會將這個服務(wù)器標(biāo)記為主觀下線。
客觀下線條件只適用于主服務(wù)器
4.2. 每個 Sentinel 都需要定期執(zhí)行的任務(wù)
- 每個 Sentinel 以每秒鐘一次的頻率向它所知的主服務(wù)器、從服務(wù)器以及其他 Sentinel 實(shí)例發(fā)送一個 PING 命令。
- 如果一個實(shí)例(instance)距離最后一次有效回復(fù) PING 命令的時間超過 down-after-milliseconds 選項所指定的值, 那么這個實(shí)例會被 Sentinel 標(biāo)記為主觀下線。 一個有效回復(fù)可以是: +PONG 、 -LOADING 或者 -MASTERDOWN 。
- 如果一個主服務(wù)器被標(biāo)記為主觀下線, 那么正在監(jiān)視這個主服務(wù)器的所有 Sentinel 要以每秒一次的頻率確認(rèn)主服務(wù)器的確進(jìn)入了主觀下線狀態(tài)。
- 如果一個主服務(wù)器被標(biāo)記為主觀下線, 并且有足夠數(shù)量的 Sentinel (至少要達(dá)到配置文件指定的數(shù)量)在指定的時間范圍內(nèi)同意這一判斷, 那么這個主服務(wù)器被標(biāo)記為客觀下線。
- 在一般情況下, 每個 Sentinel 會以每 10 秒一次的頻率向它已知的所有主服務(wù)器和從服務(wù)器發(fā)送 INFO 命令。 當(dāng)一個主服務(wù)器被 Sentinel 標(biāo)記為客觀下線時, Sentinel 向下線主服務(wù)器的所有從服務(wù)器發(fā)送 INFO 命令的頻率會從 10 秒一次改為每秒一次。
- 當(dāng)沒有足夠數(shù)量的 Sentinel 同意主服務(wù)器已經(jīng)下線, 主服務(wù)器的客觀下線狀態(tài)就會被移除。 當(dāng)主服務(wù)器重新向 Sentinel 的 PING 命令返回有效回復(fù)時, 主服務(wù)器的主管下線狀態(tài)就會被移除。
4.3 自動發(fā)現(xiàn)sentinel和slave
sentinel可以自動的發(fā)現(xiàn)其他的sentinel服務(wù)和slave節(jié)點(diǎn)。具體概念,官網(wǎng)有非常詳細(xì)的解釋
5. 代碼實(shí)現(xiàn) Jedis示例
部署架構(gòu)如上面提到的第一種方式,當(dāng)前環(huán)境:
192.168.206.200 redis slave1,sentinel1
192.168.206.201 redis master,sentinel2
192.168.206.202 redis slave2,sentinel3
redis.conf 大部分使用默認(rèn)配置,列出幾個重要的配置
bind 192.168.206.200 # 跟據(jù)master,slave對應(yīng)修改
protected-mode yes
slave-serve-stale-data yes
slave-read-only yes
slave-priority 100
min-slaves-to-write 1
min-slaves-max-lag 10
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
#slaveof 192.168.206.201 6379 在slave節(jié)點(diǎn)中才配置。master中不配置這個
sentinel.conf 三個實(shí)例一樣的配置
protected-mode no
port 26379
sentinel monitor mymaster 192.168.206.201 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
redis客戶端配置情況
package com.fun.redis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import java.util.HashSet;
import java.util.Set;
/**
* Created by fun
*
* @date 2017/5/18.
*/
public class SentinelDemo {
public static void main(String[] args) throws Exception {
Set<String> sentinels = new HashSet<String>();
sentinels.add("192.168.206.200:26379");
sentinels.add("192.168.206.201:26379");
sentinels.add("192.168.206.202:26379");
JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels);
while(true) {
System.out.println("press enter to continue");
System.in.read();
Jedis jedis = pool.getResource();
System.out.println(pool.getCurrentHostMaster());
String name = jedis.get("name");
System.out.println("redis get key name="+name);
jedis.close();
}
}
}
// output
/*
按下第一次enter的時候
press enter to continue
192.168.206.201:6379
redis get key name=fun
kill -9 殺掉master 后臺選舉,產(chǎn)生新的master 再次按下enter的時候
192.168.206.202:6379
redis get key name=fun
press enter to continue
*/
查看sentinel 的日志情況 ,可以看到切換master的過程。
4440:X 17 May 13:51:18.774 # +vote-for-leader 86a01849eb3e61289300a93a12855b6483088730 11
4440:X 17 May 13:51:19.793 # +odown master mymaster 192.168.206.201 6379 #quorum 3/2
4440:X 17 May 13:51:19.793 # Next failover delay: I will not start a failover before Wed May 17 13:57:19 2017
4440:X 17 May 13:51:19.859 # +config-update-from sentinel 86a01849eb3e61289300a93a12855b6483088730 192.168.206.200 26379 @ mymaster 192.168.206.201 6379
4440:X 17 May 13:51:19.859 # +switch-master mymaster 192.168.206.201 6379 192.168.206.202 6379
4440:X 17 May 13:51:19.859 * +slave slave 192.168.206.200:6379 192.168.206.200 6379 @ mymaster 192.168.206.202 6379
4440:X 17 May 13:51:19.860 * +slave slave 192.168.206.201:6379 192.168.206.201 6379 @ mymaster 192.168.206.202 6379
6. 問題延伸
在實(shí)際生產(chǎn)中,如果業(yè)務(wù)量比較大的情況,一臺redis實(shí)例作為緩存很有可能回出現(xiàn)不夠使用的情況,這個時候就需要做集群方式,但是redis的官方的說法是集群模式目前還處在驗證階段,沒有statble的版本出現(xiàn)。
大多時候我們都是使用sharding的方式的時候。Jedis中有ShardedJedisPool
來管理鏈接。但是如果我們對分片方式也做主從的話。加入sentinel之后。就要使用JedisSentinelPool方式管理。不難發(fā)現(xiàn),這兩種只能有一種存在。那Jedis如果做到分片情況下也能使用sentinel呢 ?
針對項目的問題,github上面有人開發(fā)了一個 ShardedJedisSentinelPool 可以用來處理這個問題。