Redis系列(四)-低成本高可用方案設計

蘑菇先生發表於2015-05-25

關於Redis高可用方案,看到較多的是keepalived、zookeeper方案。 keepalived是主備模式,意味著總有一臺浪費著。zookeeper工作量成本偏高。 本文主要介紹下使用官方sentinel做redis高可用方案的設計。

閱讀目錄:

  1. Redis Sentinel
  2. 故障轉移訊息接收的3種方式
  3. 整體流程圖
  4. 總結

Redis Sentinel

Sentinel介紹

Sentinel是Redis官方為叢集提供的高可用解決方案。 在實際專案中可以使用sentinel去做redis自動故障轉移,減少人工介入的工作量。另外sentinel也給客戶端提供了監控訊息的通知,這樣客戶端就可根據訊息型別去判斷伺服器的狀態,去做對應的適配操作。

下面是Sentinel主要功能列表:

  • Monitoring:Sentinel持續檢查叢集中的master、slave狀態,判斷是否存活。
  • Notification:在發現某個redis例項死的情況下,Sentinel能通過API通知系統管理員或其他程式指令碼。
  • Automatic failover:如果一個master掛掉後,sentinel立馬啟動故障轉移,把某個slave提升為master。其他的slave重新配置指向新master。
  • Configuration provider:對於客戶端來說sentinel通知是有效可信賴的。客戶端會連線sentinel去請求當前master的地址,一旦發生故障sentinel會提供新地址給客戶端。

Sentinel配置

Sentinel本質上只是一個執行在特殊模式下的redis伺服器,通過不同配置來區分提供服務。 sentinel.conf配置:

// [監控名稱] [ip] [port] [多少sentinel同意才發生故障轉移]
sentinel monitor mymaster 127.0.0.1 6379 2
// [監控名稱] [Master多少毫秒後不迴應ping命令,就認為master是主觀下線狀態]
sentinel down-after-milliseconds mymaster 60000
// [故障轉移超時時間]
sentinel failover-timeout mymaster 180000
//[在執行故障轉移時,最多可以有多少個從伺服器同時對新的主伺服器進行同步]
sentinel parallel-syncs mymaster 1

sentinel需要使用redis2.8版本以上,啟動如下:

redis-sentinel sentinel.conf

啟動後Sentinel會:

  • 以10秒一次的頻率,向被監視的master傳送info命令,根據回覆獲取master當前資訊。
  • 以1秒一次的頻率,向所有redis伺服器、包含sentinel在內傳送PING命令,通過回覆判斷伺服器是否線上。
  • 以2秒一次的頻率,通過向所有被監視的master,slave伺服器傳送包含當前sentinel,master資訊的訊息。

另外建議sentinel至少起3個例項以上,並配置2個例項同意即可發生轉移。 5個例項,配置3個例項同意以此類推。

故障轉移訊息接收的3種方式

Redis伺服器一旦傳送故障後,sentinel通過raft演算法投票選舉新master。 故障轉移過程可以通過sentinel的API獲取/訂閱接收事件訊息。

指令碼接收

//當故障轉移期間,可以指定一個“通知”指令碼用來告知系統管理員,當前叢集的情況。
//指令碼被允許執行的最大時間為60秒,如果超時,指令碼將會被終止(KILL)

sentinel notification-script mymaster /var/redis/notify.sh 

//故障轉移期之後,配置通知客戶端的指令碼.

sentinel client-reconfig-script mymaster /var/redis/notifyReconfig.sh 

客戶端直接接收

Sentinel的故障轉移訊息通知使用的是redis釋出訂閱(詳解Redis釋出訂閱及客戶端程式設計)。就是說在故障轉移期間所有產生的事件資訊,都通過頻道(channel)釋出出去。比如我們加臺slave伺服器,sentinel監聽到後會釋出加slave的訊息到"+slave"頻道上,客戶端只需要訂閱"+slave"頻道即可接收到對應訊息。

其訊息格式如下:
[例項型別] [事件伺服器名稱] [伺服器ip] [伺服器埠] @[master名稱] [ip] [埠]

<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>

通知訊息格式示例:

*          //訂閱型別, *即訂閱所有事件訊息。
-sdown     //訊息型別
slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381

訂閱訊息示例:

        using (RedisSentinel rs = new RedisSentinel(CurrentNode.Host, CurrentNode.Port))
            {
                var redisPubSub = new RedisPubSub(node.Host, node.Port);
                redisPubSub.OnMessage += OnMessage;
                redisPubSub.OnSuccess += (msg) =>{};
                redisPubSub.OnUnSubscribe += (obj) =>{};
                redisPubSub.OnError = (exception) =>{ };
                redisPubSub.PSubscribe("*");
            }

服務間接接收

這種方式在第二種基礎上擴充套件了一層,即應用端不直接訂閱sentinel。 單獨做服務去幹這件事情,然後應用端提供API供這個服務回撥通知。 這樣做的好處在於:

  • 減少應用端監聽失敗出錯的可能性。
  • 應用端由主動方變成被動方,降低耦合。
  • 效能提高,輪詢變回撥。
  • 獨立成服務可擴充套件性更高。

比如:

1:以後換掉sentinel,我們只需要動服務即可,應用端無需更改。

2:可以在服務內多增加一層守護執行緒去主動拉取redis狀態,這樣可確保即使sentinel不生效,也能及時察覺redis狀態,並通知到應用端。 當然這種情況很極端,因為sentinel配的也是多節點,同時掛的機率非常小。
示例:
應用端提供回撥API,在這個API邏輯下去重新整理記憶體中的Redis連線。

http://127.0.0.1/redis/notify.api

獨立服務監控到狀況後,呼叫API通知應用端:

 httprequest.post("http://127.0.0/redis/notify.api");

整體設計

推薦使用第三種,其整體流程圖如下:

總結

各種sentinel通知訊息型別見官方文件,專案中使用的redis客戶端在github上[HRedis]。本文分享了樓主在專案中做Redis高可用的經驗,希望對大家有所幫助。 在人力物力滿足的情況下還是推薦使用zookeeper方案的。 只有三五杆槍的情況下也就退而求其次,利用最小成本滿足需求並保留可擴充套件性。  

相信沒有最好的架構,只有更合適的架構。

[1] Redis Sentinel Documentation

相關文章