redis-哨兵

1、基本概念和架構

1.1 基本概念

哨兵,Redis sentinel,在主從複製的基礎上實現故障恢復的自動化。其核心功能是主節點(master)的自動故障轉移html

主要功能:java

  1. 監控(Monitor):哨兵不斷檢查主節點和從節點是否正常工做。
  2. 自動故障轉移(Automatic failover):主節點不正常時,哨兵啓動自動故障轉移,它會將失效主節點的其中一個從節點升級爲新的主節點,並讓其餘從節點從這個新的主節點複製數據。
  3. 配置提供者(Configuration provider):客戶端能夠經過哨兵來獲取主節點地址。
  4. 通知(Notification):哨兵能夠把故障轉移結果通知給客戶端。

1.2 架構

它由兩部分組成,哨兵節點和數據節點:redis

  1. 哨兵節點:哨兵系統由一個或多個哨兵節點組成,哨兵節點是特殊的redis節點,不存儲數據
  2. 數據節點:主節點和從節點都是數據節點。

redis sentinel是一個分佈式的架構,其中包含若干個sentinel節點,對於節點的故障判斷是由多個獨立的sentinel節點共同完成,這樣能夠有效防止誤判。多個sentinel節點使得即便個別sentinel節點不可用,整個sentinel節點集合依然是健壯的。算法

2、部署演示

總體部署在一臺機器上(192.168.118.129 ),經過不一樣的端口號來區分主從節點以及哨兵節點。架構

2.1 部署主從節點

部署一臺主節點(端口號爲6379),2臺從節點(端口號分別爲6380和6381)。分佈式

把6380配置爲6379的slave。ide

把6381配置爲6379的slave。函數

查看master節點的複製信息線程

2.2 部署哨兵節點

配置三個哨兵節點,和主從在同一臺機器上(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。至此,三個哨兵節點配置完成。

接下來啓動哨兵節點。有兩種方法,兩個方法等效

  1. src/redis-sentinel sentinel26379.conf
  2. src/redis-server sentinel26379.conf --sentinel

一次啓動三個哨兵,啓動後能夠查看。

啓動後哨兵節點會發現主節點,以及主節點對應的salve節點,同時哨兵節點會發現彼此。哨兵節點啓動後最終的結構以下。

2.3 故障轉移演示

演示主節點down機後哨兵進行自動故障轉移的case。目前主節點是6379節點。

殺掉主節點的進程

遷移

主節點已經被遷移成了6380節點。

轉移後配置文件也被自動作了修改。

3、客戶端鏈接(配置提供者)

哨兵做爲配置的提供者,客戶端能夠直接基於哨兵進行鏈接,而無需直到具體主節點的ip,這樣實現了和主節點的解耦,當主節點down機後,哨兵從新選主,對客戶端透明。

3.1 代碼

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節點。

3.2 原理

核心實如今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;
     }
 }

經過代碼能夠發現:

  1. 遍歷哨兵節點

  2. 鏈接哨兵節點

  3. 經過哨兵節點獲取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從新初始化鏈接池。

4、實現原理

4.1 三個定時任務

經過監控來判斷節點是否不可達,sentinel經過三個定時任務來完成後對各個節點的監控和發現。

Sentinel節點定時執行info命令來獲取拓撲信息

每隔10秒執行

  1. 經過向master 執行info能夠拿到從節點的信息(這就解釋了爲何sentinel沒有顯示配置從節點,可是能夠監發現從節點)。
  2. 從節點加入,能夠自動感知。
  3. 節點不可達或者故障轉移後,能夠經過info實時拿到拓撲信息。

Sentinel節點發布和訂閱 _sentinel_hello頻道

每隔兩秒執行

每隔2秒,每隔sentinel節點會向master的_sentinel_hello 頻道發送自身的節點信息,以及本身對master狀態的判斷,同時每一個sentinel也會訂閱改頻道,最 終實現了sentinel節點之間的互相發現以及彼此交換本身對主節點狀態的判斷,做爲客觀下線的依據。

Sentinel節點向其他節點發送ping命令

每隔1秒執行

實現對每一個節點的監控,這個定時任務是節點失敗斷定的重要依據。

4.2 主觀下線和客觀下線

一個參數: 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 節點對主節點是否下線的判斷。當超過 數量的sentinel認爲主節點已經下線的時候,就能夠把master節點表示爲客觀下線(完全下線)。

總結舉例

master至關於一個嫌疑人,在審判,法律規定必需要3(quorum)個陪審員都認爲有罪,纔會判斷爲有罪。當任何一個陪審員認爲嫌疑人有罪的時候,就問其餘陪審員的意見,若是最終彙總下來發現有3個陪審員認爲有罪,則最終纔會判斷master有罪,經過多個確認來減小誤判的可能。

客觀下線是針對master節點的,slave節點只須要主觀下線便可。

4.3 領導者Sentinel節點選舉

當最終對主節點進行客觀下線後,並非立刻進行故障轉移,而是進入領導者sentinel節點的選舉,由於最終的故障轉移由一個sentinel節點來完成,這個節點就是領導者sentinel節點。

領導者選取使用的算法是raft算法,(參考2),是一個共識算法,大體思路以下。

  1. 每一個sentinel節點都有資格成爲領導者,當它確認主節點主觀下線後,向其餘sentinel節點發送 is-master-down-by-addr,要求將本身設置爲領導者,固然給本身投了一票
  2. 其餘sentinel收到命令後,若是沒有贊成過其餘sentinel is-master-down-by-addr命令,則贊成該請求(只投一次贊成票)。
  3. 若是sentinel節點發現本身的票數已經大於或者等於max(quorum, num(sentinels)/2+1),那麼它將會成爲領導者sentinel節點
  4. 若是本次沒有選舉出領導者,將進入下一次選舉。

S1最早完成了客觀下線。

  1. 向S2請求,S2第一次收到,恢復YES
  2. 向S3請求,S3第一次收到,恢復YES

S1獲得兩票,知足條件,成爲leader。

節點 發出贊成的節點 接受贊成的節點
S1 S2,S3
S2 S1
S3 S1

4.4 故障轉移

從新選取master

當領導者sentinel選舉完成後,由該sentinel節點完成故障轉移。總體流程以下。

  1. 過濾不健康節點是指主觀下線,斷線的salve

  2. 先比較replica_priority, 值越小,優先級越高

  3. 若是priority相同,則選擇複製偏移量最大的節點(最接近原來master的節點,數據最全)

  4. 若是偏移量相同,則選擇runid最小的節點

設置過程

  1. 領導者sentinel經過執行replicaof no one命令讓選舉出來的最新節點成爲最新的master
  2. 領導者sentinel向其餘從節點發送 replicaof 命令,讓其成爲最新master的從節點
  3. 同時將已經下線的原主節點設置爲新master的從節點,等其上線後自動成爲新的主節點的從節點。
相關文章
相關標籤/搜索