哨兵,Redis sentinel,在主從複製的基礎上實現故障恢復的自動化。其核心功能是主節點(master)的自動故障轉移。html
主要功能:java
它由兩部分組成,哨兵節點和數據節點:redis
redis sentinel是一個分佈式的架構,其中包含若干個sentinel節點,對於節點的故障判斷是由多個獨立的sentinel節點共同完成,這樣能夠有效防止誤判。多個sentinel節點使得即便個別sentinel節點不可用,整個sentinel節點集合依然是健壯的。算法
總體部署在一臺機器上(192.168.118.129 ),經過不一樣的端口號來區分主從節點以及哨兵節點。架構
部署一臺主節點(端口號爲6379),2臺從節點(端口號分別爲6380和6381)。分佈式
把6380配置爲6379的slave。ide
把6381配置爲6379的slave。函數
查看master節點的複製信息線程
配置三個哨兵節點,和主從在同一臺機器上(192.168.118.129),用三個不一樣的端口號來區分(分別爲26379,26380,26381)。debug
哨兵節點是特殊的redis節點,有特殊的配置,針對每個哨兵分別創建一個配置文件。
以26379哨兵爲例,創建sentinel26379.conf配置文件,基本配置以下:
sentinel monitor mymaster 193.168.129 6379 2
這句是哨兵節點的核心配置,表明這個哨兵監控193.168.129: 6379這個master節點,且命名爲mymaster。2表明至少有兩個哨兵贊成才能認定主節點產生故障而且進行故障轉移。
按照上述步驟配置sentinel26380.conf和sentinel26381.conf。至此,三個哨兵節點配置完成。
接下來啓動哨兵節點。有兩種方法,兩個方法等效
src/redis-sentinel sentinel26379.conf
src/redis-server sentinel26379.conf --sentinel
一次啓動三個哨兵,啓動後能夠查看。
啓動後哨兵節點會發現主節點,以及主節點對應的salve節點,同時哨兵節點會發現彼此。哨兵節點啓動後最終的結構以下。
演示主節點down機後哨兵進行自動故障轉移的case。目前主節點是6379節點。
殺掉主節點的進程
遷移
主節點已經被遷移成了6380節點。
轉移後配置文件也被自動作了修改。
哨兵做爲配置的提供者,客戶端能夠直接基於哨兵進行鏈接,而無需直到具體主節點的ip,這樣實現了和主節點的解耦,當主節點down機後,哨兵從新選主,對客戶端透明。
public static void main(String[] args) { String masterName="mymaster";// 主節點名字 Set<String> sentinels=new HashSet<>();//哨兵集合 sentinels.add("192.168.118.129:26379"); sentinels.add("192.168.118.129:26380"); sentinels.add("192.168.118.129:26381"); JedisSentinelPool jedisSentinelPool=new JedisSentinelPool(masterName,sentinels); Jedis jedis= jedisSentinelPool.getResource(); jedis.set("name","cnblogs-new"); System.out.println(jedis.get("name")); jedisSentinelPool.close(); }
執行結果:
[main] INFO redis.clients.jedis.JedisSentinelPool - Trying to find master from available Sentinels... [main] INFO redis.clients.jedis.JedisSentinelPool - Redis master running at 192.168.118.129:6380, starting Sentinel listeners... [main] INFO redis.clients.jedis.JedisSentinelPool - Created JedisPool to master at 192.168.118.129:6380 cnblogs-new
能夠看到經過哨兵找到了master節點。
核心實如今JedisSentinelPool
的構造函數中
private HostAndPort initSentinels(Set<String> sentinels, final String masterName) { HostAndPort master = null; log.info("Trying to find master from available Sentinels..."); for (String sentinel : sentinels) { final HostAndPort hap = HostAndPort.parseString(sentinel); log.debug("Connecting to Sentinel {}", hap); jedis = new Jedis(hap.getHost(), hap.getPort(), sentinelConnectionTimeout, sentinelSoTimeout); List<String> masterAddr = jedis.sentinelGetMasterAddrByName(masterName); //查找master master = toHostAndPort(masterAddr); return master; } }
經過代碼能夠發現:
遍歷哨兵節點
鏈接哨兵節點
經過哨兵節點獲取master節點 sentinelGetMasterAddrByName
。改方法內部調用了redis的命令來實現 sentinel get-master-addr-by-name mymaster
上述代碼省略了一部分,訂閱。
for (String sentinel : sentinels) { final HostAndPort hap = HostAndPort.parseString(sentinel); MasterListener masterListener = new MasterListener(masterName, hap.getHost(), hap.getPort()); // whether MasterListener threads are alive or not, process can be stopped masterListener.setDaemon(true); masterListeners.add(masterListener); masterListener.start(); }
爲每個sentinel節點獨立啓動一個線程,利用redis的發佈訂閱功能,每一個線程訂閱sentinel節點上切換master的相關頻道+switch-master
public void onMessage(String channel, String message) { String[] switchMasterMsg = message.split(" "); if (masterName.equals(switchMasterMsg[0])) { initPool(toHostAndPort(Arrays.asList(switchMasterMsg[3], switchMasterMsg[4]))); } else { log.debug("Ignoring message on +switch-master for master name {}, our master name is {}",switchMasterMsg[0], masterName); } }
發現當前master發生了switch,使用initPool從新初始化鏈接池。
經過監控來判斷節點是否不可達,sentinel經過三個定時任務來完成後對各個節點的監控和發現。
Sentinel節點定時執行info命令來獲取拓撲信息
每隔10秒執行
Sentinel節點發布和訂閱 _sentinel_hello頻道
每隔兩秒執行
每隔2秒,每隔sentinel節點會向master的_sentinel_hello 頻道發送自身的節點信息,以及本身對master狀態的判斷,同時每一個sentinel也會訂閱改頻道,最 終實現了sentinel節點之間的互相發現以及彼此交換本身對主節點狀態的判斷,做爲客觀下線的依據。
Sentinel節點向其他節點發送ping命令
每隔1秒執行
實現對每一個節點的監控,這個定時任務是節點失敗斷定的重要依據。
一個參數: down-after-milliseconds
# sentinel down-after-milliseconds <master-name> <milliseconds> # # Number of milliseconds the master (or any attached replica or sentinel) should # be unreachable (as in, not acceptable reply to PING, continuously, for the # specified period) in order to consider it in S_DOWN state (Subjectively # Down). # # Default is 30 seconds.
主觀下線
上一節裏面的ping操做,若是超過 down-after-milliseconds
沒有獲得回覆,sentinel會把改節點作失敗判斷,叫作主觀下線。就是當前sentinel一家認爲這個節點是失敗的,有可能有誤判。
客觀下線
當sentinel對master作了主觀下線後會經過命令: sentinel is-master-down-by-addr
來詢問其餘sentinel 節點對主節點是否下線的判斷。當超過
總結舉例
master至關於一個嫌疑人,在審判,法律規定必需要3(quorum)個陪審員都認爲有罪,纔會判斷爲有罪。當任何一個陪審員認爲嫌疑人有罪的時候,就問其餘陪審員的意見,若是最終彙總下來發現有3個陪審員認爲有罪,則最終纔會判斷master有罪,經過多個確認來減小誤判的可能。
客觀下線是針對master節點的,slave節點只須要主觀下線便可。
當最終對主節點進行客觀下線後,並非立刻進行故障轉移,而是進入領導者sentinel節點的選舉,由於最終的故障轉移由一個sentinel節點來完成,這個節點就是領導者sentinel節點。
領導者選取使用的算法是raft算法,(參考2),是一個共識算法,大體思路以下。
is-master-down-by-addr
,要求將本身設置爲領導者,固然給本身投了一票is-master-down-by-addr
命令,則贊成該請求(只投一次贊成票)。S1最早完成了客觀下線。
S1獲得兩票,知足條件,成爲leader。
節點 | 發出贊成的節點 | 接受贊成的節點 |
---|---|---|
S1 | S2,S3 | |
S2 | S1 | |
S3 | S1 |
從新選取master
當領導者sentinel選舉完成後,由該sentinel節點完成故障轉移。總體流程以下。
過濾不健康節點是指主觀下線,斷線的salve
先比較replica_priority, 值越小,優先級越高
若是priority相同,則選擇複製偏移量最大的節點(最接近原來master的節點,數據最全)
若是偏移量相同,則選擇runid最小的節點
設置過程