redis系列--深刻哨兵集羣

1、前言

  在以前的系列文章中介紹了redis的入門、持久化以及複製功能,若是不瞭解請移步至redis系列進行閱讀,固然我也是抱着學習的知識分享,若是有什麼問題歡迎指正,也歡迎你們轉載。而本次將介紹哨兵集羣相關知識,包括哨兵集羣部署、哨兵原理、相關配置、故障轉移等內容,正由於redis有了哨兵機制,而在不少企業(包括筆者自身的公司)採用的是哨兵模式下的redis主從。html

2、哨兵(Sentinel)簡介

   哨兵(後文統稱sentinel)是官方推薦的的高可用(HA)解決方案。在以前的文章中介紹過redis的主從高可用解決方案,這種方案的缺點在於當master故障時候,須要手動進行故障恢復,而sentinel是一個獨立運行的進程,它能監控一個或多個主從集羣,並能在master故障時候自動進行故障轉移,更爲理想的是sentinel自己是一個分佈式系統,其分佈式設計思想有點相似於zookeeper,當某個時候Master故障後,sentinel集羣採用Raft算法來選取Leader,故障轉移由Leader完成。而對於客戶端來講,操做redis的主節點,咱們只須要詢問sentinel,sentinel返回當前可用的master,這樣一來客戶端不須要關注的切換而引起的客戶端配置變動。一個典型的sentinel架構以下圖:redis

 

 

sentinel的主要功能:算法

  • 監控(Monitoring): sentinel 會不斷地檢查Master和Slave是否運做正常。
  • 通知(Notification):當被監控的某個Redis實例出現問題時, 哨兵(sentinel) 能夠經過 API 向管理員或者其餘應用程序發送通知。
  • 自動故障遷移(Automatic failover):當一個Master不能正常工做時,sentinel)會開始一次自動故障遷移操做,它會將失效Master的其中一個Slave升級爲新的Master, 並讓失效Master的其餘Slave改成複製新的Master; 當客戶端試圖鏈接失效的Master時,集羣也會向客戶端返回新Master的地址,使得集羣可使用Master代替失效Master。
  • 配置中心(Configuration provider):若是故障轉移發生了,sentinel會返回新的master地址。數據庫

3、Sentinel集羣部署

環境規劃

  本次部署過程當中將分別部署三個哨兵節點來監控一主二從的redis集羣,主從的搭建過程能夠參考筆者博文《redis系列--主從複製以及redis複製演進》,如下是環境規則:安全

  • 哨兵節點:10.1.210.32:2637九、10.1.210.33:2637九、10.1.210.34:26379
  • redis實例:10.1.210.69:6379(主)、10.1.210.69:6380(從)、10.1.210.69:6381(從)

安裝配置

  sentinel安裝與redis安裝過程一致,請參考redis系列文章,而在源碼中,redis提供了參考配置示例sentinel.conf(可使用命令grep -E -v ^# sentinel.conf 查看),以下:服務器

port 26379

dir /tmp

sentinel monitor mymaster 127.0.0.1 6379 2

sentinel down-after-milliseconds mymaster 30000

sentinel parallel-syncs mymaster 1

sentinel failover-timeout mymaster 180000

配置說明:架構

sentinel monitor mymaster 127.0.0.1 6379 2

 這行配置表明sentinel監控的master名字叫作mymaster(能夠本身取),地址是127.0.0.1,端口是6379。最後一個2表明當sentinel集羣中有2個sentinel認爲master故障時候才斷定master真正不可用。官方把該參數稱爲quorum,在後續選舉領頭哨兵時候會用到,在下文將進行介紹。併發

sentinel down-after-milliseconds mymaster 30000

sentinel會向master發送心跳PING來確認master是否存活,若是master在「必定時間範圍」內不迴應PONG 或者是回覆了一個錯誤消息,那麼這個sentinel會主觀地(單方面地)認爲這個master已經不可用了(subjectively down, 也簡稱爲SDOWN)。而這個down-after-milliseconds就是用來指定這個「必定時間範圍」的,單位是毫秒,在這裏表示30秒時間內master不迴應PONG則主觀不可用。分佈式

sentinel parallel-syncs mymaster 1

該配置代表在發生failover主備切換時候,最多容許多少個slave同時同步新的master。這個數字越小,完成failover所需的時間就越長,可是若是這個數字越大,就意味着越多的slave由於replication而不可用。能夠經過將這個值設爲 1 來保證每次只有一個slave處於不能處理命令請求的狀態。ide

sentinel failover-timeout mymaster 180000

failover-time超時時間,當failover開始後,在此時間內仍然沒有觸發任何failover操做,當前sentinel將會認爲這次failover失敗,單位毫秒。

不難發現關於sentinel的配置都是固定格式以下:

sentinel <option_name> <master_name> <option_value>

 

運行Sentinel

  啓動sentinel的方式有兩種,兩種方式都必須指定配置文件:

#第一種(推薦)
redis-sentinel /path/to/sentinel.conf

#第二種
redis-server /path/to/sentinel.conf --sentinel

如下是筆者三個節點的配置文件,並同時拷貝到三個節點進行啓動:

bind 10.1.210.32 #IP地址

port 26379   #端口

dir /opt/db/redis  # 數據存儲目錄

daemonize yes  #後臺運行

logfile /opt/db/redis/sentinel.log  #日誌

sentinel monitor mymaster 10.1.210.69 6379 2

sentinel down-after-milliseconds mymaster 30000

sentinel parallel-syncs mymaster 1

sentinel failover-timeout mymaster 180000
10.1.210.32
bind 10.1.210.33 #IP地址

port 26379   #端口

dir /opt/db/redis  # 數據存儲目錄

daemonize yes  #後臺運行

logfile /opt/db/redis/sentinel.log  #日誌

sentinel monitor mymaster 10.1.210.69 6379 2

sentinel down-after-milliseconds mymaster 30000

sentinel parallel-syncs mymaster 1

sentinel failover-timeout mymaster 180000
10.1.210.33
bind 10.1.210.34 #IP地址

port 26379   #端口

dir /opt/db/redis  # 數據存儲目錄

daemonize yes  #後臺運行

logfile /opt/db/redis/sentinel.log  #日誌

sentinel monitor mymaster 10.1.210.69 6379 2

sentinel down-after-milliseconds mymaster 30000

sentinel parallel-syncs mymaster 1

sentinel failover-timeout mymaster 180000
10.1.210.34

經過redis-sentinel /opt/db/redis/sentinel.conf啓動每一個sentinel,如下是啓動日誌(能夠發現sentinel自動經過master發現slave和其餘sentinel):

 此時,一個sentinel集羣就搭建完成。

 

sentinel相關命令

  和redis同樣,sentinel能夠經過客戶端使用命令操做,例如查看master狀態SENTINEL masters,示例:

如下是全部命令以及解釋:

SENTINEL masters  #列出全部被監視的master,以及當前master狀態
SENTINEL master <master name>  #列出指定的master
SENTINEL slaves <master name>  #列出給定master的全部slave以及slave狀態
SENTINEL sentinels <master name>  #列出監控指定的master的全部sentinel
SENTINEL get-master-addr-by-name <master name>  #返回給定master名字的服務器的IP地址和端口號
SENTINEL reset <pattern>  #重置全部匹配pattern表達式的master狀態
SENTINEL failover <master name>  #當msater失效時, 在不詢問其餘 Sentinel 意見的狀況下, 強制開始一次自動故障遷移,可是它會給其餘sentinel發送一個最新的配置,其餘sentinel會根據這個配置進行更新
SENTINEL ckquorum <master name> #檢查當前sentinel的配置可否達到故障切換master所需的數量,此命令可用於檢測sentinel部署是否正常,正常返回ok
SENTINEL flushconfig #強制sentinel將運行時配置寫入磁盤,包括當前sentinel狀態

 4、Sentinel原理

SDOWN和ODOWN

  在介紹sentinel原理以前,須要瞭解的兩個概念SDOWN和ODOWN:

  • SDOWN:全拼Subjectively Down,稱爲主觀下線,指的是單個sentinel對redis實例做出的下線狀態判斷。
  • ODOWN:全拼Objectively Down,稱爲客戶端下線,指多個 Sentinel 實例在對同一個redis作出 SDOWN 判斷,而且經過 SENTINEL is-master-down-by-addr 命令互相交流以後,得出的redis實例下線判斷。(一個 Sentinel 能夠經過向另外一個 Sentinel 發送 SENTINEL is-master-down-by-addr 命令來詢問對方是否定爲給定的redis實例已下線。)

從sentinel的角度來看,若是發送了PING心跳後,在必定時間內沒有收到合法的回覆,就達到了SDOWN的條件。這個時間在配置中經過master-down-after-milliseconds參數配置。

當sentinel發送PING後,如下回復之一都被認爲是合法的:

PING replied with +PONG.
PING replied with -LOADING error.
PING replied with -MASTERDOWN error.

其它任何回覆(或者根本沒有回覆)都是不合法的。

從SDOWN切換到ODOWN不須要任何一致性算法,只須要一個gossip協議:若是一個sentinel收到了足夠多的sentinel發來消息告訴它某個master已經down掉了,SDOWN狀態就會變成ODOWN狀態。若是以後master可用了,這個狀態就會相應地被清理掉。

真正進行failover須要一個受權的過程,這個受權的過程便是leader選取過程,可是全部的failover都開始於一個ODOWN狀態。ODOWN狀態只適用於master,對於不是master的redis節點sentinel之間不須要任何協商,slaves和sentinel不會有ODOWN狀態。

 

實現原理  

  一個sentinel啓動時會讀取配置文件,並經過sentinel monitor <master-name> <ip> <port> <quorum>配置尋找要監控的主數據庫,這個配置在以前已經進行詳細說明,其中master-name是由一個大小寫字母、數字、和「._-」組成的數據庫主庫名字,爲了考慮到主庫的IP地址和端口可能在故障切換後發生變化,因此還須要ip和port來標示這個主庫。一個哨兵可監控多個主從系統從而造成網狀結構,正如在前面簡介的圖示同樣。

  sentinel啓動後,會與監控的數據庫創建兩條鏈接,以下圖(主庫10.1.210.69:6379與10.1.210.32的sentinel節點兩條連接):

這兩個鏈接與普通客戶端同樣,其中一條鏈接用來訂閱master的__sentinel__:hello頻道用於獲取其餘監控該數據庫的sentinel節點信息,另一條用於哨兵按期向主數據庫發送INFO等命令獲取主庫自己信息,緣由在於當客戶端進入訂閱模式之後只能接受消息,不能發送命令,因此還須要創建一條鏈接。

  與監控的主庫創建鏈接完成後,sentinel定時執行如下操做:

  1. 每10s會向主數據庫和從數據庫發送INFO命令;
  2. 每1s向master、slave以及其餘哨兵節點發送PING命令;
  3. 每2s向master和slave的__sentiel__:hello頻道發送本身的信息來宣佈本身的存在,同時該過程也是實現哨兵之間自動發現的基礎;

這三個操做貫穿了哨兵整個生命週期,很是重要,也是其原理的核心,因此如下將詳細介紹該操做過程。

  首先,sentinel啓動後,向主庫發送INFO命令使得sentinel能夠獲取當前主庫的相關信息(包括運行的ID,複製信息、以及屬於該主庫的從庫節點信息),這也是爲何在配置監控時候只須要配置監控的主庫信息sentinel就自動找到其對應的從庫,進而實現從庫的監控。然後和每一個從庫一樣創建兩個鏈接,這兩個鏈接和上文介紹的與主庫的連個鏈接徹底一致,在此以後,哨兵會每10s定時向已知全部主從發送INFO命令獲取信息更新並進行相應操做,好比對新增的從庫創建鏈接並加入監控隊列、又或者是主庫信息發生變化(由failover引發的)進行信息更新等。

  接下來哨兵向master和slave的__sentinel__:hello頻道發送信息與一樣監控該redis示例的其餘哨兵分享本身的信息。發送的消息內容爲:

<哨兵地址> ,<哨兵端口>,<哨兵運行的ID>,<哨兵配置的版本>,<主庫名稱>,<主庫地址>,<主庫端口>,<主庫配置版本>,該消息包含了哨兵基本信息以及監控的主庫信息,當其餘sentinel收到消息後會判斷髮消息的哨兵是否是新的哨兵,若是是則將其加入已發現的哨兵列表,並建立一個到其的鏈接(與數據庫不一樣)哨兵與哨兵之間只會建立一條鏈接用於發送PING命令,同時sentinel會判斷主數據庫的配置版本,若是該版本比記錄數據庫版本高,則更新主數據庫的數據,其做用在後續介紹。

  實現了自動發現從數據庫和其餘sentinel節點後,sentinel後續要作的任務是定時監控這些已經發現的主從節點和sentinel節點是否在線。這種監控實現方式是在經過必定時間間隔發送PING命令實現,時間間隔配置經過down-after-milliseconds指定,當超過down-after-milliseconds配置的時間後,若是被PING的數據庫或者sentinel未回覆,則哨兵認爲其主觀下線(主觀下線在上面已經介紹了),若是該節點是主庫sentinel會進一步進行判斷是否須要對其進行故障恢復(failover):sentinel會發送SENTINEL is-master-down-by-addr命令詢問其餘sentinel節點是否也認爲該主庫主觀下線,若是達到指定數量(在示例配置中也進行了說明,示例配置的是2)時,哨兵會認爲其客觀下線,並選取領頭的哨兵(leader)進行故障恢復,選舉過程後續介紹。

  選舉完零頭哨兵後,領頭哨兵會開始對主數據庫進行故障恢復,這一過程稱爲failover,在選取新的master時候,sentinel會考慮如下狀況:

  • 跟master斷開鏈接的時長 
  • slave的優先級 (由slave-priority配置指定)
  • 複製偏移量offset 
  • 實例運行的id(run id)

具體的選取順序以下:

若是一個slave跟master斷開鏈接已經超過了down-after-milliseconds的10倍,外加master宕機的時長,那麼slave就被認爲不適合選舉爲master,計算公式以下:

(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state

接下來會對slave進行排序 

  1. 按照slave優先級進行排序,slave-priority越低,優先級就越高;
  2. 若是slave priority相同,那麼比較複製偏移量,offset越靠後(越大)則代表和舊的主庫數據同步越接近,優先級就越高 ;
  3. 若是上面兩個條件都相同,那麼選擇一個run id最小的從庫;

選出從庫後,零頭哨兵將向從數據庫發送SLAVEOF NO ONE命令升級其爲新的主庫,而後在向其餘從庫發送SLAVEOF命令將從的主庫升級到最新的主庫,最後更新內部記錄將已經中止的主庫更新爲新的主庫的從庫,使得當該故障的主庫再次恢復時候自動以從庫角色繼續提供服務,從啓動到故障恢復完成這一些列過程便是哨兵的工做的完整流程也是其原理所在。

領頭哨兵選舉

  在原理中說起到了,當sentinel發現主庫客觀下線時候會進行領頭哨兵選舉進行故障恢復,其選舉算法採用Raft算法,這也爲何說其設計思想相似與zookpeer,選舉過程大致以下:

  1. 發現主庫客觀下線的哨兵節點(這裏稱爲A)向每一個哨兵節點發送命令要求對方選舉本身爲領頭哨兵(leader);
  2. 若是目標哨兵沒有選舉過其餘人,則贊成將A選舉爲領頭哨兵;
  3. 若是A發現有超過半數且超過quorum參數值的哨兵節點贊成選本身成爲領頭哨兵,則A哨兵成功選舉爲領頭哨兵。
  4. 當有多個哨兵節點同時參與領頭哨兵選舉時,出現沒有任何節點當選可能,此時每一個參選節點等待一個隨機時間進行下一輪選舉,直到選出領頭哨兵。

配置版本號做用

  一樣,在原理介紹時候說起到了master的配置版本號,當一個sentinel被受權後,它將會得到宕掉的master的一份最新配置版本號,當failover執行結束之後,這個版本號將會被用於最新的配置。由於大多數sentinel都已經知道該版本號已經被要執行failover的sentinel拿走了,因此其餘的sentinel都不能再去使用這個版本號。這意味着,每次failover都會附帶有一個獨一無二的版本號。咱們將會看到這樣作的重要性。

並且,sentinel集羣都遵照一個規則:若是sentinel A推薦sentinel B去執行failover,A會等待一段時間後,自行再次去對同一個master執行failover,這個等待的時間是經過failover-timeout配置項去配置的。從這個規則能夠看出,sentinel集羣中的sentinel不會再同一時刻併發去failover同一個master,第一個進行failover的sentinel若是失敗了,另一個將會在必定時間內進行從新進行failover,以此類推。

sentinel保證了活躍性:若是大多數sentinel可以互相通訊,最終將會有一個被受權去進行failover.
sentinel也保證了安全性:每一個試圖去failover同一個master的sentinel都會獲得一個獨一無二的版本號。

 

5、Sentinel狀態持久化

  snetinel的狀態會被持久化地寫入sentinel的配置文件中。每次當收到一個新的配置時,或者新建立一個配置時,配置會被持久化到硬盤中,並帶上配置的版本戳。這意味着,能夠安全的中止和重啓sentinel進程。下面是被重寫的配置文件截圖:

7、配置傳播

  一旦一個sentinel成功地對一個master進行了failover,它將會把關於master的最新配置經過廣播形式通知其它sentinel,其它的sentinel則更新對應master的配置,一個faiover要想被成功實行,sentinel必須可以向選爲master的slave發送SLAVE OF NO ONE命令,而後可以經過INFO命令看到新master的配置信息。

  當將一個slave選舉爲master併發送SLAVE OF NO ONE`後,即便其它的slave還沒針對新master從新配置本身,failover也被認爲是成功了的,而後全部sentinels將會發布新的配置信息。

新配在集羣中相互傳播的方式,就是爲何咱們須要當一個sentinel進行failover時必須被受權一個版本號的緣由。

每一個sentinel使用發佈/訂閱的方式持續地傳播master的配置版本信息,配置傳播的發佈/訂閱管道是:__sentinel__:hello,咱們能夠經過訂閱其頻道查看頻道中的消息,以下:

  由於每個配置都有一個版本號,因此以版本號最大的那個爲標準。例如:假設有一個名爲mymaster的地址爲10.1.210.69:6379。一開始,集羣中全部的sentinel都知道這個地址,因而爲mymaster的配置打上版本號1。一段時候後mymaster死了,有一個sentinel被受權用版本號2對其進行failover。若是failover成功了,假設地址改成了10.1.210.69:6380,此時配置的版本號爲2,進行failover的sentinel會將新配置廣播給其餘的sentinel,因爲其餘sentinel維護的版本號爲1,發現新配置的版本號爲2時,版本號變大了,說明配置更新了,因而就會採用最新的版本號爲2的配置。

 8、結束語

  從redis的入門再到哨兵模式,再到平時使用其API進行相關操做,經過一段時間的研究對redis也算有了必定層次的認識,因此把這些過程都記錄下來,分享給其餘人,但願有更多的人不只知道如何使用,更能明白其中的原理,在出問題時候能即便的定位問題。固然可能在文章中可能存在不正確的地方也歡迎你們指正,畢竟沒有源碼級別的理解。最後可能須要研究的部分就是redis的集羣,後續在研究完以後寫文章介紹,這也算對redis有一個比較全面的認識。

相關文章
相關標籤/搜索