模式二:哨兵模式
上一篇問講述了redis集群的主從模式溶褪,這一篇我們講述哨兵模式志衍。
Redis 的 Sentinel 系統(tǒng)用于管理多個 Redis 服務器(instance)悲没, 該系統(tǒng)執(zhí)行以下三個任務:
- 監(jiān)控(Monitoring): Sentinel 會不斷地檢查你的主服務器和從服務器是否運作正常。
- 提醒(Notification): 當被監(jiān)控的某個 Redis 服務器出現(xiàn)問題時第煮, Sentinel 可以通過 API 向管理員或者其他應用程序發(fā)送通知撩炊。
- 自動故障遷移(Automatic failover): 當一個主服務器不能正常工作時结耀, Sentinel 會開始一次自動故障遷移操作留夜, 它會將失效主服務器的其中一個從服務器升級為新的主服務器匙铡, 并讓失效主服務器的其他從服務器改為復制新的主服務器; 當客戶端試圖連接失效的主服務器時碍粥, 集群也會向客戶端返回新主服務器的地址鳖眼, 使得集群可以使用新主服務器代替失效服務器。
這個就相比于主從系統(tǒng)更加的靈活化嚼摩,主從系統(tǒng)一旦主節(jié)點崩潰钦讳,整個系統(tǒng)寫的功能就喪失。
Redis Sentinel 是一個分布式系統(tǒng)枕面, 你可以在一個架構中運行多個 Sentinel 進程(progress)愿卒, 這些進程使用流言協(xié)議(gossip protocols)來接收關于主服務器是否下線的信息, 并使用投票協(xié)議(agreement protocols)來決定是否執(zhí)行自動故障遷移潮秘, 以及選擇哪個從服務器作為新的主服務器琼开。
哨兵進程工作方式:
- 每個Sentinel(哨兵)進程以每秒鐘一次的頻率向整個集群中的Master主服務器,Slave從服務器以及其他Sentinel(哨兵)進程發(fā)送一個 PING 命令枕荞。
- 如果一個實例(instance)距離最后一次有效回復 PING 命令的時間超過 down-after-milliseconds 選項所指定的值柜候, 那么這個實例會被 Sentinel 標記為主觀下線。 一個有效回復可以是: +PONG 躏精、 -LOADING 或者 -MASTERDOWN 渣刷。
- 如果一個主服務器被標記為主觀下線, 那么正在監(jiān)視這個主服務器的所有 Sentinel 要以每秒一次的頻率確認主服務器的確進入了主觀下線狀態(tài)矗烛。
- 如果一個主服務器被標記為主觀下線辅柴, 并且有足夠數(shù)量的 Sentinel (至少要達到配置文件指定的數(shù)量)在指定的時間范圍內(nèi)同意這一判斷, 那么這個主服務器被標記為客觀下線高诺。
- 在一般情況下碌识, 每個 Sentinel 會以每 10 秒一次的頻率向它已知的所有主服務器和從服務器發(fā)送 INFO 命令碾篡。 當一個主服務器被 Sentinel 標記為客觀下線時虱而, Sentinel 向下線主服務器的所有從服務器發(fā)送 INFO 命令的頻率會從 10 秒一次改為每秒一次。
- 只要一個 Sentinel 發(fā)現(xiàn)某個主服務器進入了客觀下線狀態(tài)开泽, 這個 Sentinel 就可能會被其他 Sentinel 推選出牡拇, 并對失效的主服務器執(zhí)行自動故障遷移操作。
- 當沒有足夠數(shù)量的 Sentinel 同意主服務器已經(jīng)下線穆律, 主服務器的客觀下線狀態(tài)就會被移除惠呼。 當主服務器重新向 Sentinel 的 PING 命令返回有效回復時, 主服務器的主觀下線狀態(tài)就會被移除峦耘。
主觀下線以及客觀下線解釋:
主觀下線(Subjectively Down剔蹋, 簡稱 SDOWN)指的是單個 Sentinel 實例對服務器做出的下線判斷。
客觀下線(Objectively Down辅髓, 簡稱 ODOWN)指的是多個 Sentinel 實例在對同一個服務器做出 SDOWN 判斷泣崩, 并且通過 SENTINEL is-master-down-by-addr 命令互相交流之后少梁, 得出的服務器下線判斷。
從主觀下線狀態(tài)切換到客觀下線狀態(tài)并沒有使用嚴格的法定人數(shù)算法(strong quorum algorithm)矫付, 而是使用了流言協(xié)議: 如果 Sentinel 在給定的時間范圍內(nèi)凯沪, 從其他 Sentinel 那里接收到了足夠數(shù)量的主服務器下線報告, 那么 Sentinel 就會將主服務器的狀態(tài)從主觀下線改變?yōu)榭陀^下線买优。 如果之后其他 Sentinel 不再報告主服務器已下線妨马, 那么客觀下線狀態(tài)就會被移除。
客觀下線條件只適用于主服務器: 對于任何其他類型的 Redis 實例杀赢, Sentinel 在將它們判斷為下線前不需要進行協(xié)商烘跺, 所以從服務器或者其他 Sentinel 永遠不會達到客觀下線條件。
Sentinel是如何發(fā)現(xiàn)其他的Sentinel 和從服務器的脂崔?
- 每個 Sentinel 會以每兩秒一次的頻率液荸, 通過發(fā)布與訂閱功能, 向被它監(jiān)視的所有主服務器和從服務器的 sentinel:hello 頻道發(fā)送一條信息脱篙, 信息中包含了 Sentinel 的 IP 地址娇钱、端口號和運行 ID (runid)。
- 每個 Sentinel 都訂閱了被它監(jiān)視的所有主服務器和從服務器的 sentinel:hello 頻道绊困, 查找之前未出現(xiàn)過的 sentinel (looking for unknown sentinels)文搂。 當一個 Sentinel 發(fā)現(xiàn)一個新的 Sentinel 時, 它會將新的 Sentinel 添加到一個列表中秤朗, 這個列表保存了 Sentinel 已知的煤蹭, 監(jiān)視同一個主服務器的所有其他 Sentinel 。
- Sentinel 發(fā)送的信息中還包括完整的主服務器當前配置(configuration)取视。 如果一個 Sentinel 包含的主服務器配置比另一個 Sentinel 發(fā)送的配置要舊硝皂, 那么這個 Sentinel 會立即升級到新配置上。
- 在將一個新 Sentinel 添加到監(jiān)視主服務器的列表上面之前作谭, Sentinel 會先檢查列表中是否已經(jīng)包含了和要添加的 Sentinel 擁有相同運行 ID 或者相同地址(包括 IP 地址和端口號)的 Sentinel 稽物, 如果是的話, Sentinel 會先移除列表中已有的那些擁有相同運行 ID 或者相同地址的 Sentinel 折欠, 然后再添加新 Sentinel 贝或。
從上述我們可以看出哨兵之間的互相發(fā)現(xiàn)以及發(fā)現(xiàn)從服務器,都是通過發(fā)布訂閱的功能來實現(xiàn)的锐秦,既我們之前所講的redis的發(fā)布訂閱咪奖。當有一個新的哨兵加入進來就會向頻道中發(fā)送自己的信息,其他所有訂閱的哨兵會通過消費信息添加新的哨兵信息酱床。
自動故障遷移過程:
- 發(fā)現(xiàn)主服務器已經(jīng)進入客觀下線狀態(tài)羊赵。
- 在失效主服務器屬下的從服務器當中, 那些被標記為主觀下線扇谣、已斷線昧捷、或者最后一次回復 PING 命令的時間大于五秒鐘的從服務器都會被淘汰揖闸。
- 在失效主服務器屬下的從服務器當中, 那些與失效主服務器連接斷開的時長超過 down-after 選項指定的時長十倍的從服務器都會被淘汰料身。
- 在經(jīng)歷了以上兩輪淘汰之后剩下來的從服務器中汤纸, 我們選出復制偏移量(replication offset)最大的那個從服務器作為新的主服務器; 如果復制偏移量不可用芹血, 或者從服務器的復制偏移量相同贮泞, 那么帶有最小運行 ID 的那個從服務器成為新的主服務器。
- 向被選中的從服務器發(fā)送 SLAVEOF NO ONE 命令幔烛,讓它轉(zhuǎn)變?yōu)橹鞣掌鳌?/li>
- 通過發(fā)布與訂閱功能啃擦, 將更新后的配置傳播給所有其他 Sentinel , 其他 Sentinel 對它們自己的配置進行更新饿悬。
- 向已下線主服務器的從服務器發(fā)送 SLAVEOF 命令令蛉, 讓它們?nèi)椭菩碌闹鞣掌鳌?/li>
- 當所有從服務器都已經(jīng)開始復制新的主服務器時, 領頭 Sentinel 終止這次故障遷移操作狡恬。
上述簡單講述了:
- Sentinel是如何發(fā)現(xiàn)其他的Sentinel 以及從服務器
- Sentinel是如何判斷主服務器主觀下線以及客觀下線的
- Sentinel是如何自動故障遷移的
下面我們將具體的搭建哨兵模式
首先我們像搭建主從模式一樣珠叔,搭建出主從并啟動,如上一篇文章一樣弟劲,這里我們主節(jié)點為:6380端口祷安,從節(jié)點為6381端口,如圖所示:
啟動主從后兔乞,6380redis文件中創(chuàng)建哨兵模式所需要的配置文件汇鞭,需要啟動幾個哨兵就創(chuàng)建幾個配置文件,如圖:
配置文件內(nèi)容如下:
如上就是我們需要的全部配置,現(xiàn)在我們開始啟動兩個哨兵庸追。
啟動命令為 redis-service sentinel.conf --sentinel
首先啟動端口號為26379的哨兵霍骄,如圖:
啟動端口號為26380的哨兵,如圖:
此時我們查看主節(jié)點服務的模式也是哨兵模式淡溯。
并且我們可以看到兩個哨兵啟動成功后读整,兩個哨兵的配置文件也有所變化:
可以看到兩個配置文件都自動添加了從節(jié)點以及另一個哨兵的信息。
此時我們的redis哨兵模式就創(chuàng)建成功了血筑,后面我們測試主節(jié)點斷開及主節(jié)點恢復的時候和java代碼結合演示绘沉。
我們要整合哨兵模式,首先要修改redis的配置文件豺总。如下:
@Component
@Slf4j
public class JedisConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.pool.max-active}")
private int maxActive;
@Value("${spring.redis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.pool.min-idle}")
private int minIdle;
@Value("${spring.redis.pool.max-wait}")
private long maxWaitMillis;
@Bean
public JedisSentinelPool redisPoolFactory(){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
jedisPoolConfig.setMaxTotal(maxActive);
jedisPoolConfig.setMinIdle(minIdle);
//此處配置的哨兵信息,切記不要配置主節(jié)點地址择懂,這樣故障遷移的時候才能切換過來喻喳。
Set<String> sentinels = new HashSet<String>(Arrays.asList("127.0.0.1:26379","127.0.0.1:26380"));
// JedisPool jedisPool = new JedisPool(jedisPoolConfig,host,port,timeout,null);
JedisSentinelPool jedisPool = new JedisSentinelPool("mymaster",sentinels,jedisPoolConfig);
log.info("JedisPool注入成功!");
log.info("redis地址:" + host + ":" + port);
return jedisPool;
}
}
并將RedisClient中連接池修改:
//@Resource
//private JedisPool jedisPool;
@Resource
private JedisSentinelPool jedisPool;
這個具體配置之前文章中有詳細內(nèi)容
我們先測試一下哨兵模式是否連接正常
@Test
public void setRedis() {
try {
redisClient.set("testSentinel5","aaaa");
} catch (Exception e) {
e.printStackTrace();
}
}
運行結果:
可以看到連接正常困曙,那么我們現(xiàn)在將主節(jié)點關掉表伦,測試一下故障遷移谦去。
從上圖我們可以看出在6380主節(jié)點斷開后,6381從節(jié)點講過哨兵的故障遷移變?yōu)榱酥鞴?jié)點蹦哼。
并且我們可以看到原先加在6381從節(jié)點redis.windows.conf配置文件中的slaveof配置以及自動刪除了鳄哭,而且兩個哨兵中的主節(jié)點監(jiān)控也由6380變?yōu)榱?381.
我們再測試一下遷移后的寫入功能。
@Test
public void setRedis() {
try {
redisClient.set("testSentinel6","aaaa");
} catch (Exception e) {
e.printStackTrace();
}
}
可以看出在主節(jié)點斷開后纲熏,集群的寫入查看功能正常妆丘。
接下來我們將6380恢復,看是否能夠重新連接入集群:
可以看到6380重新開啟后局劲,自動變?yōu)閺墓?jié)點連接入集群勺拣,并在6380的redis.windows.conf的配置文件中自動加入了:
slaveof 10.66.205.85 6381
今天就寫到這里了,Redis的哨兵模式是以主從模式為基礎的鱼填,所以說药有,主從模式擁有的一些缺點,在哨兵模式下也具有苹丸。哨兵模式主要是監(jiān)控Master主服務器的運行情況愤惰,當然也會監(jiān)控Slave從服務器的運行情況,如果Master主服務器發(fā)生了故障赘理,該模式可以保證Slave從服務器順利升級為Master主服務器繼續(xù)提供服務羊苟,以此提高系統(tǒng)的高可用性。雖然哨兵模式比主從模式提高了不少系統(tǒng)的高可用性感憾,但是該模式不能水平擴容蜡励,不能動態(tài)的增、刪節(jié)點阻桅,這也是限制哨兵模式廣泛應用的主要原因凉倚。Redis也看到了這個情況,所在在Redis的3.x以后的版本提供了一個更加強大集群模式嫂沉,那就是Cluster集群模式稽寒,這個模式也是我們下一篇文章的主題。