深刻學習Redis(4):哨兵

前言

在 深刻學習Redis(3):主從複製 中曾提到,Redis主從複製的做用有數據熱備、負載均衡、故障恢復等;但主從複製存在的一個問題是故障恢復沒法自動化。本文將要介紹的哨兵,它基於Redis主從複製,主要做用即是解決主節點故障恢復的自動化問題,進一步提升系統的高可用性。html

文章主要內容以下:首先介紹哨兵的做用和架構;而後講述哨兵系統的部署方法,以及經過客戶端訪問哨兵系統的方法;而後簡要說明哨兵實現的基本原理;最後給出關於哨兵實踐的一些建議。文章內容基於Redis 3.0版本。java

系列文章

深刻學習Redis(1):Redis內存模型redis

深刻學習Redis(2):持久化算法

深刻學習Redis(3):主從複製sql

深刻學習Redis(4):哨兵docker

深刻學習Redis(5):集羣安全

目錄

1、做用和架構服務器

      1.做用網絡

      2. 架構架構

2、部署

      1. 部署主從節點

      2. 部署哨兵節點

      3. 演示故障轉移

      4. 總結

3、客戶端訪問哨兵系統

      1. 代碼示例

      2. 客戶端原理

      3. 總結

4、基本原理

      1. 哨兵節點支持的命令

      2. 基本原理

5、配置與實踐建議

      1. 配置

      2. 實踐建議

6、總結

1、做用和架構

1.  做用

在介紹哨兵以前,首先從宏觀角度回顧一下Redis實現高可用相關的技術。它們包括:持久化、複製、哨兵和集羣,其主要做用和解決的問題是:

  • 持久化:持久化是最簡單的高可用方法(有時甚至不被歸爲高可用的手段),主要做用是數據備份,即將數據存儲在硬盤,保證數據不會因進程退出而丟失。
  • 複製:複製是高可用Redis的基礎,哨兵和集羣都是在複製基礎上實現高可用的。複製主要實現了數據的多機備份,以及對於讀操做的負載均衡和簡單的故障恢復。缺陷:故障恢復沒法自動化;寫操做沒法負載均衡;存儲能力受到單機的限制。
  • 哨兵:在複製的基礎上,哨兵實現了自動化的故障恢復。缺陷:寫操做沒法負載均衡;存儲能力受到單機的限制。
  • 集羣:經過集羣,Redis解決了寫操做沒法負載均衡,以及存儲能力受到單機限制的問題,實現了較爲完善的高可用方案。

 

下面說回哨兵。

Redis Sentinel,即Redis哨兵,在Redis 2.8版本開始引入。哨兵的核心功能是主節點的自動故障轉移。下面是Redis官方文檔對於哨兵功能的描述:

  • 監控(Monitoring):哨兵會不斷地檢查主節點和從節點是否運做正常。
  • 自動故障轉移(Automatic failover):當主節點不能正常工做時,哨兵會開始自動故障轉移操做,它會將失效主節點的其中一個從節點升級爲新的主節點,並讓其餘從節點改成複製新的主節點。
  • 配置提供者(Configuration provider):客戶端在初始化時,經過鏈接哨兵來得到當前Redis服務的主節點地址。
  • 通知(Notification):哨兵能夠將故障轉移的結果發送給客戶端。

其中,監控和自動故障轉移功能,使得哨兵能夠及時發現主節點故障並完成轉移;而配置提供者和通知功能,則須要在與客戶端的交互中才能體現。

這裏對「客戶端」一詞在文章中的用法作一個說明:在前面的文章中,只要經過API訪問redis服務器,都會稱做客戶端,包括redis-cli、Java客戶端Jedis等;爲了便於區分說明,本文中的客戶端並不包括redis-cli,而是比redis-cli更加複雜:redis-cli使用的是redis提供的底層接口,而客戶端則對這些接口、功能進行了封裝,以便充分利用哨兵的配置提供者和通知功能。

2.  架構

典型的哨兵架構圖以下所示:

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

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

2、部署

這一部分將部署一個簡單的哨兵系統,包含1個主節點、2個從節點和3個哨兵節點。方便起見:全部這些節點都部署在一臺機器上(局域網IP:192.168.92.128),使用端口號區分;節點的配置儘量簡化。

1.  部署主從節點

哨兵系統中的主從節點,與普通的主從節點配置是同樣的,並不須要作任何額外配置。下面分別是主節點(port=6379)和2個從節點(port=6380/6381)的配置文件,配置都比較簡單,再也不詳述。

#redis-6379.conf
port 6379
daemonize yes
logfile "6379.log"
dbfilename "dump-6379.rdb"

#redis-6380.conf
port 6380
daemonize yes
logfile "6380.log"
dbfilename "dump-6380.rdb"
slaveof 192.168.92.128 6379

#redis-6381.conf
port 6381
daemonize yes
logfile "6381.log"
dbfilename "dump-6381.rdb"
slaveof 192.168.92.128 6379

配置完成後,依次啓動主節點和從節點:

redis-server redis-6379.conf
redis-server redis-6380.conf
redis-server redis-6381.conf

節點啓動後,鏈接主節點查看主從狀態是否正常,以下圖所示:

2.  部署哨兵節點

哨兵節點本質上是特殊的Redis節點。

3個哨兵節點的配置幾乎是徹底同樣的,主要區別在於端口號的不一樣(26379/26380/26381),下面以26379節點爲例介紹節點的配置和啓動方式;配置部分儘可能簡化,更多配置會在後面介紹。

#sentinel-26379.conf
port 26379
daemonize yes
logfile "26379.log"
sentinel monitor mymaster 192.168.92.128 6379 2

其中,sentinel monitor mymaster 192.168.92.128 6379 2 配置的含義是:該哨兵節點監控192.168.92.128:6379這個主節點,該主節點的名稱是mymaster,最後的2的含義與主節點的故障斷定有關:至少須要2個哨兵節點贊成,才能斷定主節點故障並進行故障轉移。

哨兵節點的啓動有兩種方式,兩者做用是徹底相同的:

redis-sentinel sentinel-26379.conf
redis-server sentinel-26379.conf --sentinel

按照上述方式配置和啓動以後,整個哨兵系統就啓動完畢了。能夠經過redis-cli鏈接哨兵節點進行驗證,以下圖所示:能夠看出26379哨兵節點已經在監控mymaster主節點(即192.168.92.128:6379),並發現了其2個從節點和另外2個哨兵節點。

此時若是查看哨兵節點的配置文件,會發現一些變化,以26379爲例:

其中,dir只是顯式聲明瞭數據和日誌所在的目錄(在哨兵語境下只有日誌);known-slave和known-sentinel顯示哨兵已經發現了從節點和其餘哨兵;帶有epoch的參數與配置紀元有關(配置紀元是一個從0開始的計數器,每進行一次領導者哨兵選舉,都會+1;領導者哨兵選舉是故障轉移階段的一個操做,在後文原理部分會介紹)。

3.  演示故障轉移

哨兵的4個做用中,配置提供者和通知須要客戶端的配合,本文將在下一章介紹客戶端訪問哨兵系統的方法時詳細介紹。這一小節將演示當主節點發生故障時,哨兵的監控和自動故障轉移功能。

(1)首先,使用kill命令殺掉主節點:

(2)若是此時當即在哨兵節點中使用info Sentinel命令查看,會發現主節點尚未切換過來,由於哨兵發現主節點故障並轉移,須要一段時間。

(3)一段時間之後,再次在哨兵節點中執行info Sentinel查看,發現主節點已經切換成6380節點。

可是同時能夠發現,哨兵節點認爲新的主節點仍然有2個從節點,這是由於哨兵在將6380切換成主節點的同時,將6379節點置爲其從節點;雖然6379從節點已經掛掉,可是因爲哨兵並不會對從節點進行客觀下線(其含義將在原理部分介紹),所以認爲該從節點一直存在。當6379節點從新啓動後,會自動變成6380節點的從節點。下面驗證一下。

(4)重啓6379節點:能夠看到6379節點成爲了6380節點的從節點。

(5)在故障轉移階段,哨兵和主從節點的配置文件都會被改寫。

對於主從節點,主要是slaveof配置的變化:新的主節點沒有了slaveof配置,其從節點則slaveof新的主節點。

對於哨兵節點,除了主從節點信息的變化,紀元(epoch)也會變化,下圖中能夠看到紀元相關的參數都+1了。

4.  總結

哨兵系統的搭建過程,有幾點須要注意:

(1)哨兵系統中的主從節點,與普通的主從節點並無什麼區別,故障發現和轉移是由哨兵來控制和完成的。

(2)哨兵節點本質上是redis節點。

(3)每一個哨兵節點,只須要配置監控主節點,即可以自動發現其餘的哨兵節點和從節點。

(4)在哨兵節點啓動和故障轉移階段,各個節點的配置文件會被重寫(config rewrite)。

(5)本章的例子中,一個哨兵只監控了一個主節點;實際上,一個哨兵能夠監控多個主節點,經過配置多條sentinel monitor便可實現。

3、客戶端訪問哨兵系統

上一小節演示了哨兵的兩大做用:監控和自動故障轉移,本小節則結合客戶端演示哨兵的另外兩個做用:配置提供者和通知。

1.  代碼示例

在介紹客戶端的原理以前,先以Java客戶端Jedis爲例,演示一下使用方法:下面代碼能夠鏈接咱們剛剛搭建的哨兵系統,並進行各類讀寫操做(代碼中只演示如何鏈接哨兵,異常處理、資源關閉等未考慮)。

public static void testSentinel() throws Exception {
         String masterName = "mymaster";
         Set<String> sentinels = new HashSet<>();
         sentinels.add("192.168.92.128:26379");
         sentinels.add("192.168.92.128:26380");
         sentinels.add("192.168.92.128:26381");

         JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels); //初始化過程作了不少工做
         Jedis jedis = pool.getResource();
         jedis.set("key1", "value1");
         pool.close();
}

2.  客戶端原理

Jedis客戶端對哨兵提供了很好的支持。如上述代碼所示,咱們只須要向Jedis提供哨兵節點集合和masterName,構造JedisSentinelPool對象;而後即可以像使用普通redis鏈接池同樣來使用了:經過pool.getResource()獲取鏈接,執行具體的命令。

在整個過程當中,咱們的代碼不須要顯式的指定主節點的地址,就能夠鏈接到主節點;代碼中對故障轉移沒有任何體現,就能夠在哨兵完成故障轉移後自動的切換主節點。之因此能夠作到這一點,是由於在JedisSentinelPool的構造器中,進行了相關的工做;主要包括如下兩點:

(1)遍歷哨兵節點,獲取主節點信息:遍歷哨兵節點,經過其中一個哨兵節點+masterName得到主節點的信息;該功能是經過調用哨兵節點的sentinel get-master-addr-by-name命令實現,該命令示例以下:

一旦得到主節點信息,中止遍歷(所以通常來講遍歷到第一個哨兵節點,循環就中止了)。

(2)增長對哨兵的監聽:這樣當發生故障轉移時,客戶端即可以收到哨兵的通知,從而完成主節點的切換。具體作法是:利用redis提供的發佈訂閱功能,爲每個哨兵節點開啓一個單獨的線程,訂閱哨兵節點的+switch-master頻道,當收到消息時,從新初始化鏈接池。

3.  總結

經過客戶端原理的介紹,能夠加深對哨兵功能的理解:

(1)配置提供者:客戶端能夠經過哨兵節點+masterName獲取主節點信息,在這裏哨兵起到的做用就是配置提供者。

須要注意的是,哨兵只是配置提供者,而不是代理。兩者的區別在於:若是是配置提供者,客戶端在經過哨兵得到主節點信息後,會直接創建到主節點的鏈接,後續的請求(如set/get)會直接發向主節點;若是是代理,客戶端的每一次請求都會發向哨兵,哨兵再經過主節點處理請求。

舉一個例子能夠很好的理解哨兵的做用是配置提供者,而不是代理。在前面部署的哨兵系統中,將哨兵節點的配置文件進行以下修改:

sentinel monitor mymaster 192.168.92.128 6379 2
改成
sentinel monitor mymaster 127.0.0.1 6379 2

而後,將前述客戶端代碼在局域網的另一臺機器上運行,會發現客戶端沒法鏈接主節點;這是由於哨兵做爲配置提供者,客戶端經過它查詢到主節點的地址爲127.0.0.1:6379,客戶端會向127.0.0.1:6379創建redis鏈接,天然沒法鏈接。若是哨兵是代理,這個問題就不會出現了。

(2)通知:哨兵節點在故障轉移完成後,會將新的主節點信息發送給客戶端,以便客戶端及時切換主節點。

4、基本原理

前面介紹了哨兵部署、使用的基本方法,本部分介紹哨兵實現的基本原理。

1.  哨兵節點支持的命令

哨兵節點做爲運行在特殊模式下的redis節點,其支持的命令與普通的redis節點不一樣。在運維中,咱們能夠經過這些命令查詢或修改哨兵系統;不過更重要的是,哨兵系統要實現故障發現、故障轉移等各類功能,離不開哨兵節點之間的通訊,而通訊的很大一部分是經過哨兵節點支持的命令來實現的。下面介紹哨兵節點支持的主要命令。

(1)基礎查詢:經過這些命令,能夠查詢哨兵系統的拓撲結構、節點信息、配置信息等。

  • info sentinel:獲取監控的全部主節點的基本信息
  • sentinel masters:獲取監控的全部主節點的詳細信息
  • sentinel master mymaster:獲取監控的主節點mymaster的詳細信息
  • sentinel slaves mymaster:獲取監控的主節點mymaster的從節點的詳細信息
  • sentinel sentinels mymaster:獲取監控的主節點mymaster的哨兵節點的詳細信息
  • sentinel get-master-addr-by-name mymaster:獲取監控的主節點mymaster的地址信息,前文已有介紹
  • sentinel is-master-down-by-addr:哨兵節點之間能夠經過該命令詢問主節點是否下線,從而對是否客觀下線作出判斷

(2)增長/移除對主節點的監控

sentinel monitor mymaster2 192.168.92.128 16379 2:與部署哨兵節點時配置文件中的sentinel monitor功能徹底同樣,再也不詳述

sentinel remove mymaster2:取消當前哨兵節點對主節點mymaster2的監控

(3)強制故障轉移

sentinel failover mymaster:該命令能夠強制對mymaster執行故障轉移,即使當前的主節點運行無缺;例如,若是當前主節點所在機器即將報廢,即可以提早經過failover命令進行故障轉移。

2.  基本原理

關於哨兵的原理,關鍵是瞭解如下幾個概念。

(1)定時任務:每一個哨兵節點維護了3個定時任務。定時任務的功能分別以下:經過向主從節點發送info命令獲取最新的主從結構;經過發佈訂閱功能獲取其餘哨兵節點的信息;經過向其餘節點發送ping命令進行心跳檢測,判斷是否下線。

(2)主觀下線:在心跳檢測的定時任務中,若是其餘節點超過必定時間沒有回覆,哨兵節點就會將其進行主觀下線。顧名思義,主觀下線的意思是一個哨兵節點「主觀地」判斷下線;與主觀下線相對應的是客觀下線。

(3)客觀下線:哨兵節點在對主節點進行主觀下線後,會經過sentinel is-master-down-by-addr命令詢問其餘哨兵節點該主節點的狀態;若是判斷主節點下線的哨兵數量達到必定數值,則對該主節點進行客觀下線。

須要特別注意的是,客觀下線是主節點纔有的概念;若是從節點和哨兵節點發生故障,被哨兵主觀下線後,不會再有後續的客觀下線和故障轉移操做。

(4)選舉領導者哨兵節點:當主節點被判斷客觀下線之後,各個哨兵節點會進行協商,選舉出一個領導者哨兵節點,並由該領導者節點對其進行故障轉移操做。

監視該主節點的全部哨兵都有可能被選爲領導者,選舉使用的算法是Raft算法;Raft算法的基本思路是先到先得:即在一輪選舉中,哨兵A向B發送成爲領導者的申請,若是B沒有贊成過其餘哨兵,則會贊成A成爲領導者。選舉的具體過程這裏不作詳細描述,通常來講,哨兵選擇的過程很快,誰先完成客觀下線,通常就能成爲領導者。

(5)故障轉移:選舉出的領導者哨兵,開始進行故障轉移操做,該操做大致能夠分爲3個步驟:

  • 在從節點中選擇新的主節點:選擇的原則是,首先過濾掉不健康的從節點;而後選擇優先級最高的從節點(由slave-priority指定);若是優先級沒法區分,則選擇複製偏移量最大的從節點;若是仍沒法區分,則選擇runid最小的從節點。
  • 更新主從狀態:經過slaveof no one命令,讓選出來的從節點成爲主節點;並經過slaveof命令讓其餘節點成爲其從節點。
  • 將已經下線的主節點(即6379)設置爲新的主節點的從節點,當6379從新上線後,它會成爲新的主節點的從節點。

 

經過上述幾個關鍵概念,能夠基本瞭解哨兵的工做原理。爲了更形象的說明,下圖展現了領導者哨兵節點的日誌,包括從節點啓動到完成故障轉移。

5、配置與實踐建議

1.  配置

下面介紹與哨兵相關的幾個配置。

(1) sentinel monitor {masterName} {masterIp} {masterPort} {quorum}

sentinel monitor是哨兵最核心的配置,在前文講述部署哨兵節點時已說明,其中:masterName指定了主節點名稱,masterIp和masterPort指定了主節點地址,quorum是判斷主節點客觀下線的哨兵數量閾值:當斷定主節點下線的哨兵數量達到quorum時,對主節點進行客觀下線。建議取值爲哨兵數量的一半加1。

(2) sentinel down-after-milliseconds {masterName} {time}

sentinel down-after-milliseconds與主觀下線的判斷有關:哨兵使用ping命令對其餘節點進行心跳檢測,若是其餘節點超過down-after-milliseconds配置的時間沒有回覆,哨兵就會將其進行主觀下線。該配置對主節點、從節點和哨兵節點的主觀下線斷定都有效。

down-after-milliseconds的默認值是30000,即30s;能夠根據不一樣的網絡環境和應用要求來調整:值越大,對主觀下線的斷定會越寬鬆,好處是誤判的可能性小,壞處是故障發現和故障轉移的時間變長,客戶端等待的時間也會變長。例如,若是應用對可用性要求較高,則能夠將值適當調小,當故障發生時儘快完成轉移;若是網絡環境相對較差,能夠適當提升該閾值,避免頻繁誤判。

(3) sentinel parallel-syncs {masterName} {number}

sentinel parallel-syncs與故障轉移以後從節點的複製有關:它規定了每次向新的主節點發起復制操做的從節點個數。例如,假設主節點切換完成以後,有3個從節點要向新的主節點發起復制;若是parallel-syncs=1,則從節點會一個一個開始複製;若是parallel-syncs=3,則3個從節點會一塊兒開始複製。

parallel-syncs取值越大,從節點完成複製的時間越快,可是對主節點的網絡負載、硬盤負載形成的壓力也越大;應根據實際狀況設置。例如,若是主節點的負載較低,而從節點對服務可用的要求較高,能夠適量增長parallel-syncs取值。parallel-syncs的默認值是1。

(4) sentinel failover-timeout {masterName} {time}

sentinel failover-timeout與故障轉移超時的判斷有關,可是該參數不是用來判斷整個故障轉移階段的超時,而是其幾個子階段的超時,例如若是主節點晉升從節點時間超過timeout,或從節點向新的主節點發起復制操做的時間(不包括複製數據的時間)超過timeout,都會致使故障轉移超時失敗。

failover-timeout的默認值是180000,即180s;若是超時,則下一次該值會變爲原來的2倍。

(5)除上述幾個參數外,還有一些其餘參數,如安全驗證相關的參數,這裏不作介紹。

2.  實踐建議

(1)哨兵節點的數量應不止一個,一方面增長哨兵節點的冗餘,避免哨兵自己成爲高可用的瓶頸;另外一方面減小對下線的誤判。此外,這些不一樣的哨兵節點應部署在不一樣的物理機上。

(2)哨兵節點的數量應該是奇數,便於哨兵經過投票作出「決策」:領導者選舉的決策、客觀下線的決策等。

(3)各個哨兵節點的配置應一致,包括硬件、參數等;此外,全部節點都應該使用ntp或相似服務,保證時間準確、一致。

(4)哨兵的配置提供者和通知客戶端功能,須要客戶端的支持才能實現,如前文所說的Jedis;若是開發者使用的庫未提供相應支持,則可能須要開發者本身實現。

(5)當哨兵系統中的節點在docker(或其餘可能進行端口映射的軟件)中部署時,應特別注意端口映射可能會致使哨兵系統沒法正常工做,由於哨兵的工做基於與其餘節點的通訊,而docker的端口映射可能致使哨兵沒法鏈接到其餘節點。例如,哨兵之間互相發現,依賴於它們對外宣稱的IP和port,若是某個哨兵A部署在作了端口映射的docker中,那麼其餘哨兵使用A宣稱的port沒法鏈接到A。

6、總結

本文首先介紹了哨兵的做用:監控、故障轉移、配置提供者和通知;而後講述了哨兵系統的部署方法,以及經過客戶端訪問哨兵系統的方法;再而後簡要說明了哨兵實現的基本原理;最後給出了關於哨兵實踐的一些建議。

在主從複製的基礎上,哨兵引入了主節點的自動故障轉移,進一步提升了Redis的高可用性;可是哨兵的缺陷一樣很明顯:哨兵沒法對從節點進行自動故障轉移,在讀寫分離場景下,從節點故障會致使讀服務不可用,須要咱們對從節點作額外的監控、切換操做。

此外,哨兵仍然沒有解決寫操做沒法負載均衡、及存儲能力受到單機限制的問題;這些問題的解決須要使用集羣,我將在後面的文章中介紹,歡迎關注。

參考文獻

https://redis.io/topics/sentinel

http://www.redis.cn/

《Redis開發與運維》

《Redis設計與實現》

 

創做不易,若是文章對你有幫助,就點個贊、評個論唄~

創做不易,若是文章對你有幫助,就點個贊、評個論唄~

創做不易,若是文章對你有幫助,就點個贊、評個論唄~

相關文章
相關標籤/搜索