Redis哨兵模式


Redis Sentinel

  • Redis Sentinel是一個分布式系統,Redis Sentinel為Redis提供高可用性。可以在沒有人為干預的情況下 阻止某種類型的故障。
  • 可以在一個架構中運行多個 Sentinel 進程(progress), 這些進程使用流言協議(gossip protocols)來 接收關於主服務器是否下線的信息, 並使用投票協議(agreement protocols)來決定是否執行自動故 障遷移, 以及選擇哪個從服務器作為新的主服務器。

Redis 的 Sentinel 系統用於管理多個 Redis 服務器(instance) 該系統執行以下三個任務:

  • 監控(Monitoring): Sentinel 會不斷地定期檢查你的主服務器和從服務器是否運作正常。
  • 提醒(Notification): 當被監控的某個 Redis 服務器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程序發送通知。
  • 自動故障遷移(Automaticfailover): 當一個主服務器不能正常工作時, Sentinel 會開始一次自動故障遷移操作, 它會將失效主服務器的其中 一個從服務器升級為新的主服務器, 並讓失效主服務器的其他從服務器改為復制新的主服務器; 當客 戶端試圖連接失效的主服務器時, 集群也會向客戶端返回新主服務器的地址, 使得集群可以使用新主 服務器代替失效服務器

Redis Sentinel 高可用性

  • 當主節點出現故障時redis sentinel 能自動完成故障發現和故障轉移,並通知客戶端從而實現真正的高可用。
  • Redis Sentinel是一個分布式架構,其中包含N個Sentinel節點和Redis數 據節點,每個Sentinel節點會對數據節點和其他Sentinel節點進行監控, 當它返現節點不可達時,會對節點做下線標識,如果被標識的是主節 點,它還會和其他Sentinel及誒單進行“協商”,當大多數節點都認為主 節點不可達時,會選舉出一個Sentinel節點來完成自動故障轉移的工作, 同時會將這個變化實時通知給redis的客戶端,整個過程是自動的不需 要人工干預,有效的解決了redis的高可用問題。
  • 同時看出,Redis Sentinel包含多個Sentinel節點,這樣做帶來兩個好處:
    • 對於節點的故障判斷是由多個節點共同完成,這樣可以有效的防止誤判斷
    • 多個Sentinel節點出現個別節點不可用,也不會影響客戶端的訪問

Sentinel的“仲裁會”

前面我們談到,當一個master被sentinel集群監控時,需要為它指定一個參數,這個參數指定了當需要判決master為不可用,並且進行failover時,所需要的sentinel數量,本文中我們暫時稱這個參數為票數

不過,當failover主備切換真正被觸發后,failover並不會馬上進行,還需要sentinel中的大多數sentinel授權后才可以進行failover。
當ODOWN時,failover被觸發。failover一旦被觸發,嘗試去進行failover的sentinel會去獲得“大多數”sentinel的授權(如果票數比大多數還要大的時候,則詢問更多的sentinel)
這個區別看起來很微妙,但是很容易理解和使用。例如,集群中有5個sentinel,票數被設置為2,當2個sentinel認為一個master已經不可用了以后,將會觸發failover,但是,進行failover的那個sentinel必須先獲得至少3個sentinel的授權才可以實行failover。
如果票數被設置為5,要達到ODOWN狀態,必須所有5個sentinel都主觀認為master為不可用,要進行failover,那么得獲得所有5個sentinel的授權。

配置版本號

為什么要先獲得大多數sentinel的認可時才能真正去執行failover呢?

當一個sentinel被授權后,它將會獲得宕掉的master的一份最新配置版本號,當failover執行結束以后,這個版本號將會被用於最新的配置。因為大多數sentinel都已經知道該版本號已經被要執行failover的sentinel拿走了,所以其他的sentinel都不能再去使用這個版本號。這意味着,每次failover都會附帶有一個獨一無二的版本號。我們將會看到這樣做的重要性。

而且,sentinel集群都遵守一個規則:如果sentinel A推薦sentinel B去執行failover,B會等待一段時間后,自行再次去對同一個master執行failover,這個等待的時間是通過failover-timeout配置項去配置的。從這個規則可以看出,sentinel集群中的sentinel不會再同一時刻並發去failover同一個master,第一個進行failover的sentinel如果失敗了,另外一個將會在一定時間內進行重新進行failover,以此類推。

redis sentinel保證了活躍性:如果大多數sentinel能夠互相通信,最終將會有一個被授權去進行failover.
redis sentinel也保證了安全性:每個試圖去failover同一個master的sentinel都會得到一個獨一無二的版本號。

配置傳播

一旦一個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的地址為192.168.56.11:6379。一開始,集群中所有的sentinel都知道這個地址,於是為mymaster的配置打上版本號1。一段時候后mymaster死了,有一個sentinel被授權用版本號2對其進行failover。如果failover成功了,假設地址改為了192.168.56.12:6279,此時配置的版本號為2,進行failover的sentinel會將新配置廣播給其他的sentinel,由於其他sentinel維護的版本號為1,發現新配置的版本號為2時,版本號變大了,說明配置更新了,於是就會采用最新的版本號為2的配置。

這意味着sentinel集群保證了第二種活躍性:一個能夠互相通信的sentinel集群最終會采用版本號最高且相同的配置。

SDOWN和ODOWN的更多細節

  • 主觀下線(Subjectively Down, 簡稱 SDOWN)指的是單個 Sentinel 實例對服務器做出的下線判斷。
  • 客觀下線(Objectively Down, 簡稱 ODOWN)指的是多個 Sentinel 實例在對同一個服務器做出 SDOWN 判斷, 並且通過 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服務器下線判斷。 (一個 Sentinel 可以通過向另一個 Sentinel 發送 SENTINEL is-master-down-by-addr命令來詢問對方是否認為給定的服務器已下線。)

從sentinel的角度來看,如果發送了PING心跳后,在master-down-after-milliseconds時間內沒有收到合法的回復,就達到了SDOWN的條件。

當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需要一個授權的過程,但是所有的failover都開始於一個ODOWN狀態。

ODOWN狀態只適用於master,對於不是master的redis節點sentinel之間不需要任何協商,slaves和sentinel不會有ODOWN狀態。

每個 Sentinel 都需要定期執行的任務

  • 每個 Sentinel 以每秒鍾一次的頻率向它所知的主服務器、從服務器以及其他 Sentinel 實例發送一個 PING 命令。
  • 如果一個實例(instance)距離最后一次有效回復 PING 命令的時間超過 down-after-milliseconds 選項所指定的值, 那么這個實例會被 Sentinel 標記為主觀下線。 一個有效回復可以是: +PONG 、 -LOADING 或者 -MASTERDOWN 。
  • 如果一個主服務器被標記為主觀下線, 那么正在監視這個主服務器的所有 Sentinel 要以每秒一次的頻率確認主服務器的確進入了主觀下線狀態。
  • 如果一個主服務器被標記為主觀下線, 並且有足夠數量的 Sentinel (至少要達到配置文件指定的數量)在指定的時間范圍內同意這一判斷, 那么這個主服務器被標記為客觀下線。
  • 在一般情況下, 每個 Sentinel 會以每 10 秒一次的頻率向它已知的所有主服務器和從服務器發送 INFO 命令。 當一個主服務器被 Sentinel 標記為客觀下線時, Sentinel 向下線主服務器的所有從服務器發送 INFO 命令的頻率會從 10 秒一次改為每秒一次。
  • 當沒有足夠數量的 Sentinel 同意主服務器已經下線, 主服務器的客觀下線狀態就會被移除。 當主服務器重新向 Sentinel 的 PING 命令返回有效回復時, 主服務器的主管下線狀態就會被移除。

Sentinel之間和Slaves之間的自動發現機制

雖然sentinel集群中各個sentinel都互相連接彼此來檢查對方的可用性以及互相發送消息。但是你不用在任何一個sentinel配置任何其它的sentinel的節點。因為sentinel利用了master的發布/訂閱機制去自動發現其它也監控了統一master的sentinel節點。

通過向名為__sentinel__:hello的管道中發送消息來實現。

同樣,你也不需要在sentinel中配置某個master的所有slave的地址,sentinel會通過詢問master來得到這些slave的地址的。

  • 每個 Sentinel 會以每兩秒一次的頻率, 通過發布與訂閱功能, 向被它監視的所有主服務器和從服務器的 __sentinel__:hello 頻道發送一條信息, 信息中包含了 Sentinel 的 IP 地址、端口號和運行 ID (runid)。
  • 每個 Sentinel 都訂閱了被它監視的所有主服務器和從服務器的 __sentinel__:hello 頻道, 查找之前未出現過的 sentinel (looking for unknown sentinels)。 當一個 Sentinel 發現一個新的 Sentinel 時, 它會將新的 Sentinel * 添加到一個列表中, 這個列表保存了 Sentinel 已知的, 監視同一個主服務器的所有其他 Sentinel 。
  • Sentinel 發送的信息中還包括完整的主服務器當前配置(configuration)。 如果一個 Sentinel 包含的主服務器配置比另一個 Sentinel 發送的配置要舊, 那么這個 Sentinel 會立即升級到新配置上。
  • 在將一個新 Sentinel 添加到監視主服務器的列表上面之前, Sentinel 會先檢查列表中是否已經包含了和要添加的 Sentinel 擁有相同運行 ID 或者相同地址(包括 IP 地址和端口號)的 Sentinel , 如果是的話, Sentinel 會先移除列表中已有的那些擁有相同運行 ID 或者相同地址的 Sentinel , 然后再添加新 Sentinel 。

網絡隔離時的一致性

redis sentinel集群的配置的一致性模型為最終一致性,集群中每個sentinel最終都會采用最高版本的配置。然而,在實際的應用環境中,有三個不同的角色會與sentinel打交道:

Redis實例.
Sentinel實例.
客戶端.
為了考察整個系統的行為我們必須同時考慮到這三個角色。

下面有個簡單的例子,有三個主機,每個主機分別運行一個redis和一個sentinel:

             +-------------+ | Sentinel 1 | <--- Client A | Redis 1 (M) | +-------------+ | | +-------------+ | +------------+ | Sentinel 2 |-----+-- / partition / ----| Sentinel 3 | <--- Client B | Redis 2 (S) | | Redis 3 (M)| +-------------+ +------------+

在這個系統中,初始狀態下redis3是master, redis1和redis2是slave。之后redis3所在的主機網絡不可用了,sentinel1和sentinel2啟動了failover並把redis1選舉為master。

Sentinel集群的特性保證了sentinel1和sentinel2得到了關於master的最新配置。但是sentinel3依然持着的是就的配置,因為它與外界隔離了。

當網絡恢復以后,我們知道sentinel3將會更新它的配置。但是,如果客戶端所連接的master被網絡隔離,會發生什么呢?

客戶端將依然可以向redis3寫數據,但是當網絡恢復后,redis3就會變成redis的一個slave,那么,在網絡隔離期間,客戶端向redis3寫的數據將會丟失。

也許你不會希望這個場景發生:

如果你把redis當做緩存來使用,那么你也許能容忍這部分數據的丟失。
但如果你把redis當做一個存儲系統來使用,你也許就無法容忍這部分數據的丟失了。
因為redis采用的是異步復制,在這樣的場景下,沒有辦法避免數據的丟失。然而,你可以通過以下配置來配置redis3和redis1,使得數據不會丟失。

min-slaves-to-write 1 min-slaves-max-lag 10

通過上面的配置,當一個redis是master時,如果它不能向至少一個slave寫數據(上面的min-slaves-to-write指定了slave的數量),它將會拒絕接受客戶端的寫請求。由於復制是異步的,master無法向slave寫數據意味着slave要么斷開連接了,要么不在指定時間內向master發送同步數據的請求了(上面的min-slaves-max-lag指定了這個時間)。

故障轉移

一次故障轉移操作由以下步驟組成:

  • 發現主服務器已經進入客觀下線狀態。
  • 對我們的當前紀元進行自增(詳情請參考 Raft leader election ), 並嘗試在這個紀元中當選。
  • 如果當選失敗, 那么在設定的故障遷移超時時間的兩倍之后, 重新嘗試當選。 如果當選成功, 那么執行以下步驟。
  • 選出一個從服務器,並將它升級為主服務器。
  • 向被選中的從服務器發送 SLAVEOF NO ONE 命令,讓它轉變為主服務器。
  • 通過發布與訂閱功能, 將更新后的配置傳播給所有其他 Sentinel , 其他 Sentinel 對它們自己的配置進行更新。
  • 向已下線主服務器的從服務器發送 SLAVEOF 命令, 讓它們去復制新的主服務器。
  • 當所有從服務器都已經開始復制新的主服務器時, 領頭 Sentinel 終止這次故障遷移操作。
每當一個 Redis 實例被重新配置(reconfigured) —— 無論是被設置成主服務器、從服務器、又或者被設置成其他主服務器的從服務器 —— Sentinel 都會向被重新配置的實例發送一個 CONFIG REWRITE 命令, 從而確保這些配置會持久化在硬盤里。

Sentinel 使用以下規則來選擇新的主服務器:

  • 在失效主服務器屬下的從服務器當中, 那些被標記為主觀下線、已斷線、或者最后一次回復 PING 命令的時間大於五秒鍾的從服務器都會被淘汰。
  • 在失效主服務器屬下的從服務器當中, 那些與失效主服務器連接斷開的時長超過 down-after 選項指定的時長十倍的從服務器都會被淘汰。
  • 在經歷了以上兩輪淘汰之后剩下來的從服務器中, 我們選出復制偏移量(replication offset)最大的那個從服務器作為新的主服務器; 如果復制偏移量不可用, 或者從服務器的復制偏移量相同, 那么帶有最小運行 ID 的那個從服務器成為新的主服務器。

Sentinel狀態持久化

snetinel的狀態會被持久化地寫入sentinel的配置文件中。每次當收到一個新的配置時,或者新創建一個配置時,配置會被持久化到硬盤中,並帶上配置的版本戳。這意味着,可以安全的停止和重啟sentinel進程。

安裝和部署Redis Sentinel

角色 IP 端口
Master,sentinel1 192.168.56.11 6379,26379
Slave 01,sentinel2 192.168.56.12 6379,26379
Slave 02,sentinel3 192.168.56.13 6379,26379

配置 redis 集群

cat redis_6379.conf protected-mode yes bind 192.168.56.12 port 6379 daemonize yes supervised no pidfile "/var/run/redis_6379.pid" loglevel notice logfile "/data/app/redis/logs/redis_6379.log" databases 16 dbfilename "dump_6379.rdb" dir "/data/db/redis_6379"

配置redis實例
cd /data/app/redis/conf
啟動: /data/app/redis/bin/redis-server redis_6379.conf
檢查是否啟動: /data/app/redis/bin/redis-cli -h 192.168.56.11 -p 6379 ping
配置主從:

/data/app/redis/bin/redis-cli -h 192.168.56.12 -p 6379 /data/app/redis/bin/redis-cli -h 192.168.56.13 -p 6379 >SLAVEOF 192.168.56.11 6379 >CONFIG REWRITE #寫入配置文件 

Master確認主從: /home/mdb/redis/src/redis-cli -h 192.168.56.11 -p 6379

role:master connected_slaves:2 slave0:ip=192.168.56.12,port=6379,state=online,offset=239,lag=1 slave1:ip=192.168.56.13,port=6379,state=online,offset=239,lag=1 master_repl_offset:239

配置 Sentinel

sentinel 節點啟動有兩種方式:

  1. 使用 redis-sentinel sentinel_6379.conf
  2. 使用redis-server sentinel_6379.conf --sentinel
port 26379 daemonize no bind 192.168.56.11 logfile "/data/app/redis/logs/sentinel_26379.log" dir "/data/db/sentinel_26379" sentinel monitor mymaster 192.168.56.11 6379 2 sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 180000

接下來我們將一行一行地解釋上面的配置項:

sentinel monitor mymaster 192.168.56.11 6379 2

這一行代表sentinel監控的master的名字叫做mymaster,地址為192.168.56.11:6379,行尾最后的一個2代表什么意思呢?我們知道,網絡是不可靠的,有時候一個sentinel會因為網絡堵塞而誤以為一個master redis已經死掉了,當sentinel集群式,解決這個問題的方法就變得很簡單,只需要多個sentinel互相溝通來確認某個master是否真的死了,這個2代表,當集群中有2個sentinel認為master死了時,才能真正認為該master已經不可用了。(sentinel集群中各個sentinel也有互相通信,通過gossip協議)。

除了第一行配置,我們發現剩下的配置都有一個統一的格式:

sentinel <option_name> <master_name> <option_value>

接下來我們根據上面格式中的option_name一個一個來解釋這些配置項:

down-after-milliseconds

sentinel會向master發送心跳PING來確認master是否存活,如果master在“一定時間范圍”內不回應PONG 或者是回復了一個錯誤消息,那么這個sentinel會主觀地(單方面地)認為這個master已經不可用了(subjectively down, 也簡稱為SDOWN)。而這個down-after-milliseconds就是用來指定這個“一定時間范圍”的,單位是毫秒。
不過需要注意的是,這個時候sentinel並不會馬上進行failover主備切換,這個sentinel還需要參考sentinel集群中其他sentinel的意見,如果超過某個數量的sentinel也主觀地認為該master死了,那么這個master就會被客觀地(注意哦,這次不是主觀,是客觀,與剛才的subjectively down相對,這次是objectively down,簡稱為ODOWN)認為已經死了。需要一起做出決定的sentinel數量在上一條配置中進行配置。

parallel-syncs

在發生failover主備切換時,這個選項指定了最多可以有多少個slave同時對新的master進行同步,這個數字越小,完成failover所需的時間就越長,但是如果這個數字越大,就意味着越多的slave因為replication而不可用。可以通過將這個值設為 1 來保證每次只有一個slave處於不能處理命令請求的狀態。
其他配置項在sentinel.conf中都有很詳細的解釋。
所有的配置都可以在運行時用命令SENTINEL SET command動態修改。

啟動: /data/app/redis/bin/redis-sentinel /data/app/redis/conf/sentinel_26379.conf

cat ../logs/sentinel_26379.log 99344:X 16 Oct 16:20:59.156 # Sentinel ID is f16a463d7387bf71f5ebce0c969d01d5bd802ac4 99344:X 16 Oct 16:20:59.156 # +monitor master mymaster 192.168.56.11 6379 quorum 2 99344:X 16 Oct 16:20:59.156 * +slave slave 192.168.56.12:6379 192.168.56.12 6379 @ mymaster 192.168.56.11 6379 99344:X 16 Oct 16:20:59.157 * +slave slave 192.168.56.13:6379 192.168.56.13 6379 @ mymaster 192.168.56.11 6379 99344:X 16 Oct 16:21:01.087 * +sentinel sentinel eb2582f3d12d8ed7710a94e6555a858047a91d2e 192.168.56.12 26379 @ mymaster 192.168.56.11 6379 99344:X 16 Oct 16:21:01.119 * +sentinel sentinel 2a716b1f6e6e9ab6688a99160e7a6616b913336b 192.168.56.13 26379 @ mymaster 192.168.56.11 6379

當所有節點啟動以后,配置文件發生了變化,sentinel發現了從節點和其余的 sentinel 節點 去掉了默認的故障轉移,復制參數,

port 26379 daemonize no bind 192.168.56.12 logfile "/data/app/redis/logs/sentinel_26379.log" dir "/data/db/sentinel_26379" sentinel myid eb2582f3d12d8ed7710a94e6555a858047a91d2e sentinel monitor mymaster 192.168.56.13 6379 2 sentinel config-epoch mymaster 1 sentinel leader-epoch mymaster 1 # Generated by CONFIG REWRITE sentinel known-slave mymaster 192.168.56.12 6379 sentinel known-slave mymaster 192.168.56.11 6379 sentinel known-sentinel mymaster 192.168.56.11 26379 f16a463d7387bf71f5ebce0c969d01d5bd802ac4 sentinel known-sentinel mymaster 192.168.56.13 26379 2a716b1f6e6e9ab6688a99160e7a6616b913336b sentinel current-epoch 1

模擬故障轉移

/data/app/redis/bin/redis-cli -h 192.168.56.12 -p 26379 192.168.56.12:26379> info master0:name=mymaster,status=ok,address=192.168.56.11:6379,slaves=2,sentinels=3 192.168.56.12:26379> sentinel failover mymaster OK 192.168.56.12:26379> info master0:name=mymaster,status=ok,address=192.168.56.13:6379,slaves=2,sentinels=3

查看 sentinel 日志:

21808:X 16 Oct 16:24:54.045 # Executing user requested FAILOVER of 'mymaster'
21808:X 16 Oct 16:24:54.045 # +new-epoch 1
21808:X 16 Oct 16:24:54.045 # +try-failover master mymaster 192.168.56.11 6379
21808:X 16 Oct 16:24:54.084 # +vote-for-leader eb2582f3d12d8ed7710a94e6555a858047a91d2e 1
21808:X 16 Oct 16:24:54.084 # +elected-leader master mymaster 192.168.56.11 6379
21808:X 16 Oct 16:24:54.084 # +failover-state-select-slave master mymaster 192.168.56.11 6379 21808:X 16 Oct 16:24:54.137 # +selected-slave slave 192.168.56.13:6379 192.168.56.13 6379 @ mymaster 192.168.56.11 6379 21808:X 16 Oct 16:24:54.137 * +failover-state-send-slaveof-noone slave 192.168.56.13:6379 192.168.56.13 6379 @ mymaster 192.168.56.11 63 79 21808:X 16 Oct 16:24:54.214 * +failover-state-wait-promotion slave 192.168.56.13:6379 192.168.56.13 6379 @ mymaster 192.168.56.11 6379 21808:X 16 Oct 16:24:55.177 # +promoted-slave slave 192.168.56.13:6379 192.168.56.13 6379 @ mymaster 192.168.56.11 6379 21808:X 16 Oct 16:24:55.177 # +failover-state-reconf-slaves master mymaster 192.168.56.11 6379 21808:X 16 Oct 16:24:55.243 * +slave-reconf-sent slave 192.168.56.12:6379 192.168.56.12 6379 @ mymaster 192.168.56.11 6379 21808:X 16 Oct 16:24:56.215 * +slave-reconf-inprog slave 192.168.56.12:6379 192.168.56.12 6379 @ mymaster 192.168.56.11 6379 21808:X 16 Oct 16:24:56.215 * +slave-reconf-done slave 192.168.56.12:6379 192.168.56.12 6379 @ mymaster 192.168.56.11 6379 21808:X 16 Oct 16:24:56.317 # +failover-end master mymaster 192.168.56.11 6379 21808:X 16 Oct 16:24:56.317 # +switch-master mymaster 192.168.56.11 6379 192.168.56.13 6379 21808:X 16 Oct 16:24:56.318 * +slave slave 192.168.56.12:6379 192.168.56.12 6379 @ mymaster 192.168.56.13 6379 21808:X 16 Oct 16:24:56.318 * +slave slave 192.168.56.11:6379 192.168.56.11 6379 @ mymaster 192.168.56.13 6379

模擬故障轉移
查看 sentinel 配置文件更新變化和 redis 主節點變化

sentinel monitor mymaster 192.168.56.13 6379 2 sentinel config-epoch mymaster 1 sentinel leader-epoch mymaster 1 # Generated by CONFIG REWRITE sentinel known-slave mymaster 192.168.56.12 6379 sentinel known-slave mymaster 192.168.56.11 6379 sentinel known-sentinel mymaster 192.168.56.11 26379 f16a463d7387bf71f5ebce0c969d01d5bd802ac4 sentinel known-sentinel mymaster 192.168.56.13 26379 2a716b1f6e6e9ab6688a99160e7a6616b913336b sentinel current-epoch 1

注意事項

  1. sentinel 節點不要部署在同一台機器
  2. 至少不是三個且奇數個的 sentinel 節點,增加選舉的准確性因為領導者選舉需要至少一半加1個節點
  3. sentinel節點集合可以只監控一個主節點,也可以監控多個主節點, 盡量使用一套sentinel監控一個主節點。
  4. sentinel的數據節點與普通的 redis 數據節點沒有區別
  5. 客戶端初始化連接的是 Sentinel節點集合,不再是具體的 redis 節 點,但是Sentinel 是配置中心不是代理。

Sentinel日常運維

Sentinel常用命令

以下列出的是Sentinel接受的命令:

  • PING:返回PONG。
  • SENTINEL master <master name>:用於查看監控的某個Redis Master信息,包括配置和狀態等。
  • SENTINEL slaves <master name>:列出給定主服務器的所有從服務器,以及這些從服務器的當前狀態。
  • SENTINEL sentinels <master name>:查看給定主服務器的Sentinel實例列表及其狀態。
  • SENTINEL get-master-addr-by-name <master name>:返回給定名字的主服務器的IP地址和端口號。 如果這個主服務器正在執行故障轉移操作,或者針對這個主服務器的故障轉移操作已經完成,那么這個命令返回新的主服務器的IP地址和端口號。
  • SENTINEL reset <pattern>:重置所有名字和給定模式pattern相匹配的主服務器。pattern 參數是一個Glob風格的模式。重置操作清除主服務器目前的所有狀態,包括正在執行中的故障轉移,並移除目前已經發現和關聯的,主服務器的所有從服務器和Sentinel。
  • SENTINEL failover <master name>:當主服務器失效時, 在不詢問其他Sentinel意見的情況下, 強制開始一次自動故障遷移(不過發起故障轉移的Sentinel會向其他Sentinel發送一個新的配置,其他Sentinel會根據這個配置進行相應的更新)。
  • SENTINEL reset <pattern>:強制重設所有監控的Master狀態,清除已知的Slave和Sentinel實例信息,重新獲取並生成配置文件。
  • SENTINEL failover <master name>:強制發起一次某個Master的failover,如果該Master不可訪問的話。
  • SENTINEL ckquorum <master name>:檢測Sentinel配置是否合理,failover的條件是否可能滿足,主要用來檢測你的Sentinel配置是否正常。
  • SENTINEL flushconfig:強制Sentinel重寫所有配置信息到配置文件。
  • SENTINEL is-master-down-by-addr <ip> <port>:一個Sentinel可以通過向另一個Sentinel發送SENTINEL is-master-down-by-addr命令來詢問對方是否認為給定的服務器已下線。

動態修改Sentinel配置

以下是一些修改sentinel配置的命令:

  • SENTINEL MONITOR <name> <ip> <port> <quorum>這個命令告訴sentinel去監聽一個新的master
  • SENTINEL REMOVE <name> 命令sentinel放棄對某個master的監聽
  • SENTINEL SET <name> <option> <value> 這個命令很像Redis的CONFIG SET命令,用來改變指定master的配置。支持多個

增加和移除Sentinel

增加新的Sentinel實例非常簡單,修改好配置文件,啟動即可,其他Sentinel會自動發現該實例並加入集群。如果要批量啟動一批Sentinel節點,最好以30秒的間隔一個一個啟動為好,這樣能確保整個 Sentinel集群的大多數能夠及時感知到新節點,滿足當時可能發生的選舉條件。

移除一個Sentinel實例會相對麻煩一些,因為Sentinel不會忘記已經感知到的Sentinel實例,所以最好按照下列步驟來處理:

停止將要移除的sentinel進程。
給其余的sentinel進程發送SENTINEL RESET *命令來重置狀態,忘記將要移除的sentinel,每個進程之間間隔30秒。
確保所有sentinel對於當前存貨的sentinel數量達成一致,可以通過SENTINEL MASTER <mastername>命令來觀察,或者查看配置文件。

刪除舊master或者不可達slave

sentinel永遠會記錄好一個Master的slaves,即使slave已經與組織失聯好久了。這是很有用的,因為sentinel集群必須有能力把一個恢復可用的slave進行重新配置。

並且,failover后,失效的master將會被標記為新master的一個slave,這樣的話,當它變得可用時,就會從新master上復制數據。

然后,有時候你想要永久地刪除掉一個slave(有可能它曾經是個master),你只需要發送一個SENTINEL RESET master命令給所有的sentinels,它們將會更新列表里能夠正確地復制master數據的slave。

發布/訂閱

客戶端可以向一個sentinel發送訂閱某個頻道的事件的命令,當有特定的事件發生時,sentinel會通知所有訂閱的客戶端。需要注意的是客戶端只能訂閱,不能發布。

訂閱頻道的名字與事件的名字一致。例如,頻道名為sdown 將會發布所有與SDOWN相關的消息給訂閱者。

如果想要訂閱所有消息,只需簡單地使用PSUBSCRIBE *

以下是所有你可以收到的消息的消息格式,如果你訂閱了所有消息的話。第一個單詞是頻道的名字,其它是數據的格式。

注意:以下的instance details的格式是:

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

如果這個redis實例是一個master,那么@之后的消息就不會顯示。

+reset-master <instance details> -- 當master被重置時. +slave <instance details> -- 當檢測到一個slave並添加進slave列表時. +failover-state-reconf-slaves <instance details> -- Failover狀態變為reconf-slaves狀態時 +failover-detected <instance details> -- 當failover發生時 +slave-reconf-sent <instance details> -- sentinel發送SLAVEOF命令把它重新配置時 +slave-reconf-inprog <instance details> -- slave被重新配置為另外一個master的slave,但數據復制還未發生時。 +slave-reconf-done <instance details> -- slave被重新配置為另外一個master的slave並且數據復制已經與master同步時。 -dup-sentinel <instance details> -- 刪除指定master上的冗余sentinel時 (當一個sentinel重新啟動時,可能會發生這個事件). +sentinel <instance details> -- 當master增加了一個sentinel時。 +sdown <instance details> -- 進入SDOWN狀態時; -sdown <instance details> -- 離開SDOWN狀態時。 +odown <instance details> -- 進入ODOWN狀態時。 -odown <instance details> -- 離開ODOWN狀態時。 +new-epoch <instance details> -- 當前配置版本被更新時。 +try-failover <instance details> -- 達到failover條件,正等待其他sentinel的選舉。 +elected-leader <instance details> -- 被選舉為去執行failover的時候。 +failover-state-select-slave <instance details> -- 開始要選擇一個slave當選新master時。 no-good-slave <instance details> -- 沒有合適的slave來擔當新master selected-slave <instance details> -- 找到了一個適合的slave來擔當新master failover-state-send-slaveof-noone <instance details> -- 當把選擇為新master的slave的身份進行切換的時候。 failover-end-for-timeout <instance details> -- failover由於超時而失敗時。 failover-end <instance details> -- failover成功完成時。 switch-master <master name> <oldip> <oldport> <newip> <newport> -- 當master的地址發生變化時。通常這是客戶端最感興趣的消息了。 +tilt -- 進入Tilt模式。 -tilt -- 退出Tilt模式。

TILT 模式

redis sentinel非常依賴系統時間,例如它會使用系統時間來判斷一個PING回復用了多久的時間。
然而,假如系統時間被修改了,或者是系統十分繁忙,或者是進程堵塞了,sentinel可能會出現運行不正常的情況。
當系統的穩定性下降時,TILT模式是sentinel可以進入的一種的保護模式。當進入TILT模式時,sentinel會繼續監控工作,但是它不會有任何其他動作,它也不會去回應is-master-down-by-addr這樣的命令了,因為它在TILT模式下,檢測失效節點的能力已經變得讓人不可信任了。
如果系統恢復正常,持續30秒鍾,sentinel就會退出TITL模式。

-BUSY狀態

注意:該功能還未實現。

當一個腳本的運行時間超過配置的運行時間時,sentinel會返回一個-BUSY 錯誤信號。如果這件事發生在觸發一個failover之前,sentinel將會發送一個SCRIPT KILL命令,如果script是只讀的話,就能成功執行。

生產環境推薦

對於一個最小集群,Redis應該是一個Master帶上兩個Slave,並且開啟下列選項:

min-slaves-to-write 1 min-slaves-max-lag 10

這樣能保證寫入Master的同時至少寫入一個Slave,如果出現網絡分區阻隔並發生failover的時候,可以保證寫入的數據最終一致而不是丟失,寫入老的Master會直接失敗。

Slave可以適當設置優先級,除了0之外(0表示永遠不提升為Master),越小的優先級,越有可能被提示為Master。如果Slave分布在多個機房,可以考慮將和Master同一個機房的Slave的優先級設置的更低以提升他被選為新的Master的可能性。

考慮到可用性和選舉的需要,Sentinel進程至少為3個,推薦為5個。如果有網絡分區,應當適當分布(比如2個在A機房, 2個在B機房,一個在C機房)等。

客戶端實現

客戶端從過去直接連接Redis ,變成:

先連接一個Sentinel實例
使用 SENTINEL get-master-addr-by-name master-name 獲取Redis地址信息。
連接返回的Redis地址信息,通過ROLE命令查詢是否是Master。如果是,連接進入正常的服務環節。否則應該斷開重新查詢。
(可選)客戶端可以通過SENTINEL sentinels 來更新自己的Sentinel實例列表。
當Sentinel發起failover后,切換了新的Master,Sentinel會發送 CLIENT KILL TYPE normal命令給客戶端,客戶端需要主動斷開對老的Master的鏈接,然后重新查詢新的Master地址,再重復走上面的流程。這樣的方式仍然相對不夠實時,可以通過Sentinel提供的Pub/Sub來更快地監聽到failover事件,加快重連。

如果需要實現讀寫分離,讀走Slave,那可以走SENTINEL slaves 來查詢Slave列表並連接。

其他
由於Redis是異步復制,所以Sentinel其實無法達到強一致性,它承諾的是最終一致性:最后一次failover的Redis Master贏者通吃,其他Slave的數據將被丟棄,重新從新的Master復制數據。此外還有前面提到的分區帶來的一致性問題。

其次,Sentinel的選舉算法依賴時間,因此要確保所有機器的時間同步,如果發現時間不一致,Sentinel實現了一個TITL模式來保護系統的可用性。

from redis.sentinel import Sentinel sentinel = Sentinel([('localhost', 26379)], socket_timeout=0.1) print(sentinel.discover_master('mymaster')) print(sentinel.discover_slaves('mymaster')) master = sentinel.master_for('mymaster', socket_timeout=0.1) master.set('foo', 'bar') slave = sentinel.slave_for('mymaster', socket_timeout=0.1) slave.get('foo') 'bar'


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM