IT技術互動交流平臺

架構設計:系統存儲(17)Redis集群方案:高可用

來源:IT165收集  發布日期:2016-12-22 20:36:19

1、概述

從本篇文章開始,我們將向讀者介紹幾種Redis的高可用高負載集群方案。除了介紹Redis 3.X版本中推薦的原生集群方案外,還會介紹使用第三方組件搭建Redis集群的方法。本文我們會首先介紹Redis的高可用集群方案。

2、Redis高可用方案

Redis提供的高可用方案和我們介紹過的很多軟件的高可用方案類似,都是使用主從節點的思路。即是有一個Master節點在平時提供服務,另外一個或多個Slave節點在平時不提供服務(或只提供數據讀取服務)。當Master節點由于某些原因停止服務后,再人工/自動完成Slave節點到Master節點的切換工作,以便整個Redis集群繼續向外提供服務。既然是要進行角色切換,且要求這些節點針對外部調用者來說沒有任何不同,最重要的就是Master節點和Slave節點的數據同步過程。數據同步最關鍵的設計思路是如何在數據一致性和同步性能上找到一個完美的平衡點。

同步復制的工作思路可以概括為:Master節點的任何數據變化都會立即同步到一個或多個Slave節點上。只要一個Slave節點同步失。ɡ绯瑫r),都會認為整個數據寫操作過程失敗。這樣的設計考慮側重于保證各節點上的數據絕對一致,完全沒有考慮對Master節點的響應性能,甚至會出現Master節點為了保證數據一致性而停止對后續寫操作請求的響應。

異步復制的工作思路可以概括為:Master節點首先保證對外部請求的響應性能,它和Slave節點的數據同步一般由一個新的進程/線程獨立完成。數據復制過程由Slave節點周期性發起或者由它一直駐留在Master節點的連接進行實時監控又或者由Master節點主動推送數據,再或者是同時使用多個異步復制過程。由于在Slave節點進行數據同步時,Master節點一直在處理新的數據寫請求,所以Slave節點已完成同步的數據和Master上的實時數據一般會存在一些差異。例如MySQL原生支持的數據復制過程,就是一個異步過程。

很顯然異步復制思路在對調用者的響應性能上,表現要比同步復制好得多。但如果由于異步復制而導致的節點間數據差異達到某種程度,就失去了數據同步的意義了。所以如何減少節點間的數據差異就成為異步復制過程中需要關注的要點。而后者的處理辦法就有很多了,例如MySQL由第三方插件支持的半同步方式,又例如講解ActiveMQ消息隊列時提到的AutoAck和DUPS_OK_ACK,再例如我們下文介紹的Diskless Replication和Master寫保護。

2-1、主從復制工作過程

Redis的主從復制功能除了支持一個Master節點對應多個Slave節點的同時進行復制外,還支持Slave節點向其它多個Slave節點進行復制。這樣就使得架構師能夠靈活組織業務緩存數據的傳播,例如使用多個Slave作為數據讀取服務的同時,專門使用一個Slave節點為流式分析工具服務。Redis的主從復制功能分為兩種數據同步模式:全量數據同步和增量數據同步。

這里寫圖片描述

上圖簡要說明了Redis眾喎?http://www.shiekolong605.icu/pro/pkqt/" target="_blank" class="keylink">QTWFzdGVyvdq147W9U2xhdmW92rXjtcTIq8G/yv2+3c2ssr25/bPMoaO1sVNsYXZlvdq147j4tqi1xHJ1bl9pZLrNTWFzdGVytcRydW5faWSyu9K71sLKsaOsu/LV31NsYXZluPi2qLXEyc/Su7TO1PbBv82ssr21xG9mZnNldLXEzrvWw9TaTWFzdGVytcS7t9DOxNq05tbQzt63qLaozrvKsaOouvPOxLvhzOG1vaOpo6xNYXN0ZXK+zbvhttRTbGF2ZbeixvDIq8G/zayyvbLZ1/eho9XiyrE8c3Ryb25nPs7ewtvE+srHt/HU2k1hc3RlcrTyv6rBy1JEQr/s1dW5psTco6zL/LrNU2xhdmW92rXjtcTDv9K7tM7Iq8G/zayyvbLZ1/e5/bPMtry74bj80MIvtLS9qE1hc3RlcsnPtcRSRELOxLz+PC9zdHJvbmc+oaPU2lNsYXZlway907W9TWFzdGVyo6yyos3qs8m12tK7tM7Iq8G/yv2+3c2ssr2686OsvdPPwsC0TWFzdGVytb1TbGF2ZbXEyv2+3c2ssr25/bPM0ruw477NysfU9sG/zayyvdDOyr3By6Oo0rKzxs6qsr+31s2ssr2jqaGj1PbBv82ssr25/bPMsrvU2db30qrSwMC1UkRCzsS8/qOsTWFzdGVyu+G9q9DCsvrJ+rXEyv2+3bHku6+y2df3tOa3xdTa0ru49sTatObH+NPyo6zV4rj2xNq05sf40/KyydPDu7fQzrm51Oyho7n9s8zI58/Co7o8L3A+CjxwPjxpbWcgYWx0PQ=="這里寫圖片描述" src="http://www.shiekolong605.icu/uploadfile/files/2016/1222/20161222193742342.png" title="" />

為什么在Master上新增的數據除了根據Master節點上RDB或者AOF的設置進行日志文件更新外,還會同時將數據變化寫入一個環形內存結構,并以后者為依據進行Slave節點的增量更新呢?主要原因有以下幾個:

由于網絡環境的不穩定,網絡抖動/延遲都可能造成Slave和Master暫時斷開連接,這種情況要遠遠多于新的Slave連接到Master的情況。如果以上所有情況都使用全量更新,就會大大增加Master的負載壓力——寫RDB文件是有大量I/O過程的,雖然Linux Page Cahe特性會減少性能消耗。

另外在數據量達到一定規模的情況下,使用全量更新進行和Slave的第一次同步是一個不得已的選擇——因為要盡快減少Slave節點和Master節點的數據差異。所以只能占用Master節點的資源和網絡帶寬資源。

使用內存記錄數據增量操作,可以有效減少Master節點在這方面付出的I/O代價。而做成環形內存的原因,是為了保證在滿足數據記錄需求的情況下盡可能減少內存的占用量。這個環形內存的大小,可以通過repl-backlog-size參數進行設置。

Slave重連后會向Master發送之前接收到的Master run_id信息和上一次完成部分同步的offset的位置信息。如果Master能夠確定這個run_id和自己的run_id一致且能夠在環形內存中找到這個offset的位置,Master就會發送從offset的位置開始向Slave發送增量數據。那么連接正常的各個Slave節點如何接受新數據呢?連接正常的Slave節點將會在Master節點將數據寫入環形內存后,主動接收到來自Master的數據復制信息。

2-2、基本Master/Slave配置

Redis提供的主從復制功能的配置信息,在Redis主配置文件的“REPLICATION”部分。以下是這個部分的主要參數項說明:

slaveof <masterip> <masterport>:如果您需要將某個節點設置為某個Master節點的Slave節點,您需要在這里指定Master節點的IP信息和端口信息。這個設置項默認是關閉的,也即是說Master節點不需要設置這個參數。另外,除了通過配置文件設置外,您還可以通過Redis的客戶端命令進行slaveof設定。

slave-serve-stale-data:當master節點斷開和當前salve節點的連接或者當前slave節點正在進行和master節點的數據同步時,如果收到了客戶端的數據讀取請求,slave服務器是否使用陳舊數據向客戶端提供服務。該參數的默認值為yes。

slave-read-only 是否將salve節點設置為“只讀”。一旦設置為“只讀”,表示這個Salve節點只會進行數據讀取服務,如果客戶端直接向這個Salve節點發送寫數據的請求,則會收到錯誤提示。建議采用默認的“yes”值進行設定。

repl-diskless-sync:上文已經介紹過Redis的主從復制功能基于RDB,后者的過程是將數據刷入RDB文件(實際上是Linux的Page Cache區域),然后基于RDB文件內容的更新情況和Salve當前已同步的數據標記點來進行Salve上的數據更新。所以這個過程實際會增加一定的數據延遲,消耗一定的處理資源;谶@個情況,Redis中提供了一種不經過物理磁盤設備就進行主從數據同步的技術,稱為diskless。但是直到Redis version 3.2這個技術也一直處于試驗狀態,所以并不推薦在生產環境下使用:“
WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY”。

repl-diskless-sync-delay:這個參數只有在上一個參數設置為“yes”時才起作用,主要是設置在進行兩次diskless模式的數據同步操作的時間間隔。默認為5秒。

repl-ping-slave-period:Slave節點向Master節點發送ping指令的事件間隔,默認為10秒。

repl-timeout:這是一個超時間,當某些操作達到這個時間時,Master和Slave雙方都會認為對方已經斷開連接。實際上您可以將這個時間看成是一個租約到期的時間。那么這個操作時間會影響哪些操作呢?A、向Slave進行的數據同步操作本身不能超過這個時間;B、Slave向Master發送一個PING指令并等待響應的時間;C、Master向Slave發送PONG回復并等待ACK的時間。

repl-disable-tcp-nodelay:這個選項的默認值為no,它對優化主從復制時使用的網絡資源非常有用。要明白這個參數的含義,就首先要解釋一下tcp-nodelay是個什么玩意兒?TCP數據報的報文頭包含很多屬性,這些屬性基本上起到記錄和保證傳輸目的、傳輸狀態的作用,但沒有數據報的所攜帶的業務數據(稱之為有效載荷)。那么很明顯,20個字節內容的信息分成20個數據報進行傳輸和只用一個數據報進行傳輸,需要占用的網絡資源就完全不一樣。JohnNagle在1984年發明了一種減輕網絡傳輸壓力的算法,就是為了解決這個問題(算法的名字就叫做“Nagle”,后續的技術人員又做了很多改進和升級)。其基本思路就是將要發送的內容湊夠一定的數量后,再用一個數據報發送出去。如果該屬性設置為yes,Redis將使用“Nagle”算法(或類似算法),讓數據報中的有效載荷湊夠一定數量后,在發送出去;設置成no,Redis就不會這么做。

repl-backlog-size:上文已經介紹過了Redis中為了進行增量同步所準備的環形內存區域,以及Redis這樣做的原因額,所以這里就不再贅述了。這個選項就是用來設置環形內存的大小的,這個選項的默認值為1MB;正式的生產環境下可以稍微加大一些,例如5MB。

slave-priority:當前Slave節點的優先級權重。我們后文會介紹一款Redis自帶的監控和故障轉移工具:Redis Sentinel,這個工具允許一個Master節點下有多個Slave節點,并且可以自動切換Slave節點為Master節點。如果Slave節點的優先級權重值越低,就會再切換時有限成為新的Master節點。

min-slaves-to-write和min-slaves-max-lag:為了盡可能避免Master節點對應的多個Slave節點在數據復制過程中數據差異被越拉越大。Redis服務提供了一組拒絕數據寫操作的策略,這個策略可以解釋為:當Master上在min-slaves-max-lag時間(單位秒)間隔后,任然有min-slaves-to-write個Slave和它正常連接,那么Master才允許進行數據寫操作。

2-3、Master和Slave設置實例

討論了Redis中主從復制的基本原理和Redis主配置文件中針對主從復制的設定選項意義后,我們來看一個實際設置過程。注意,由于這個過程非常簡單所以我們會“非?”。首先Master服務器不需要針對主從復制做任何的設置(這不包括對主從復制過程的配置優化)。所以我們就直接來看Slave節點的配置:

Slave節點上我們只需要做一件事情,就是打開slaveof選項:
......
# slaveof選項的設置,給定master節點的ip和port就可以了
# 192.168.61.140就是master節點
slaveof 192.168.61.140 6379
......
接著,我們馬上就可以看看同步效果了。首先確保您的master節點使工作正常的,然后就可以啟動Slave節點了:
......
5349:S 17 Dec 04:20:00.773 * Connecting to MASTER 192.168.61.140:6379
5349:S 17 Dec 04:20:00.773 * MASTER <-> SLAVE sync started
5349:S 17 Dec 04:20:00.774 * Non blocking connect for SYNC fired the event.
5349:S 17 Dec 04:20:00.775 * Master replied to PING, replication can continue...
5349:S 17 Dec 04:20:00.776 * Partial resynchronization not possible (no cached master)
5349:S 17 Dec 04:20:00.782 * Full resync from master: 976f0b31cbf6acd4fcc888301ea4639a7c591136:1
5349:S 17 Dec 04:20:00.864 * MASTER <-> SLAVE sync: receiving 119 bytes from master
5349:S 17 Dec 04:20:00.865 * MASTER <-> SLAVE sync: Flushing old data
5349:S 17 Dec 04:20:00.865 * MASTER <-> SLAVE sync: Loading DB in memory
5349:S 17 Dec 04:20:00.865 * MASTER <-> SLAVE sync: Finished with success
5349:S 17 Dec 04:20:01.068 * Background append only file rewriting started by pid 5352
5349:S 17 Dec 04:20:01.082 * AOF rewrite child asks to stop sending diffs.
5352:C 17 Dec 04:20:01.082 * Parent agreed to stop sending diffs. Finalizing AOF...
5352:C 17 Dec 04:20:01.082 * Concatenating 0.00 MB of AOF diff received from parent.
5352:C 17 Dec 04:20:01.082 * SYNC append only file rewrite performed
5352:C 17 Dec 04:20:01.082 * AOF rewrite: 6 MB of memory used by copy-on-write
5349:S 17 Dec 04:20:01.168 * Background AOF rewrite terminated with success
5349:S 17 Dec 04:20:01.168 * Residual parent diff successfully flushed to the rewritten AOF (0.00 MB)
5349:S 17 Dec 04:20:01.168 * Background AOF rewrite finished successfully
......

筆者在Slave節點上開啟了定期的RDB快照和AOF日志功能,所以各位讀者可以忽略那些日志信息,直接關注“Connecting to MASTER ….”和“MASTER <-> SLAVE …….”這些日志信息就好。

以下是Master節點上給出的日志信息
......
5614:M 17 Dec 04:20:00.789 * Slave 192.168.61.145:6379 asks for synchronization
5614:M 17 Dec 04:20:00.789 * Full resync requested by slave 192.168.61.145:6379
5614:M 17 Dec 04:20:00.789 * Starting BGSAVE for SYNC with target: disk
5614:M 17 Dec 04:20:00.791 * Background saving started by pid 5620
5620:C 17 Dec 04:20:00.814 * DB saved on disk
5620:C 17 Dec 04:20:00.815 * RDB: 6 MB of memory used by copy-on-write
5614:M 17 Dec 04:20:00.875 * Background saving terminated with success
5614:M 17 Dec 04:20:00.877 * Synchronization with slave 192.168.61.145:6379 succeeded
......

看來Master節點收到了Slave節點的連接信息,并完成了全量數據同步操作。

2-4、關閉RDB功能的說明

以上介紹的Master節點和Slave節點的設置是否特別簡單?是的,實際上只需要打開了Slave節點上“REPLICATION”區域的slaveof選項就可以讓Redis的主從復制功能運作起來,F在我們往回倒,回到上一篇文章的介紹。在上一篇文章介紹RDB快照功能的配置項時,文章提到了可以用以下方式關閉RDB快照功能:

# 以下為默認的設置為,注釋掉即可
# save 900 1
# save 300 10
# save 60 10000
# 在設置以下選項,就可以關閉RDB功能
save ''

但是根據本文對Redis主從復制的介紹,我們可以發現Redis的RDB快照功能實際上是無法真正關閉的!以上所謂關閉RDB功能的設置,只是關閉了Redis服務在正常工作時定期快照的條件設定,但只要有Slave節點請求全量數據同步,Master節點就會強制做一次RDB快照。并且如果客戶端主動發送BGSAVE命令,要求Redis服務進行RDB快照時,Redis也會被動執行RDB快照操作。

但是本文還是建議在組建Redis高可用集群時,關閉Master節點上的RDB功能。讀者一定要清楚這樣做的原因:這不是為了像個別網絡資料說的那樣真正關閉Redis的RDB快照功能,而是盡可能減少Master上主動進行RDB操作的次數,并將RDB快照工作轉移到各個Slave節點完成。

3、Redis Sentinel

Redis服務提供了性能較高的主從復制功能,但是沒有提供原生的Master——Slave的切換功能。也就是說如果您只是配置了Redis的主從復制功能,那么在Master節點出現故障時,您必須手動將一臺Slave狀態的節點切換為Master狀態。當然這個問題在Redis Version 2.8 版本前是有標準解決方案的,那就是:Keepalived + Redis服務組成的高可用集群。

這里寫圖片描述

由Keepalived監控Redis高可用集群眾喎?http://www.shiekolong605.icu/pro/pkqt/" target="_blank" class="keylink">QTWFzdGVyvdq147XEuaTX99e0zKyjrLKi1NrS7LOjx+m/9s/Cx9C7u8Ht0ru49r3ateO908zmuaTX96GjtavKx6Os1eK49re9sLjKx9PQ0rvQqc7KzOLBy6OsxuTW0Nau0ru+zcrHy/nT0LXEU2xhdmW92rXj1NpTdGFuZGJ517TMrMqxzt63qLfWtaNNYXN0ZXK92rXjtcTIzrrO0NTE3NG5waYmbWRhc2g7Jm1kYXNoO7y0yrnE+sno1sPBy3JlYWQtb25sebXIss7K/dKysrvQ0KOs0vLOqlZJULj5sb6yu7vhsNHH68fzx9C5/ciloaOyosfS1eLW1re9yr27ubK7zKu3vbHjvOC/2FJlZGlzuN+/ydPDvK/IutbQuPe49rf+zvG92rXjtcTKtcqx17TMrKGjPC9wPgo8cD6001ZlcnNpb24gMi44sOaxvr+qyryjrFJlZGlzzOG5qcHL0ru49tStyfq1xNb3tNPXtMysvOC/2LrNx9C7u7XE1+m8/iZtZGFzaDsmbWRhc2g7UmVkaXMgU2VudGluZWyho82ouf3L/Ly8yvXIy9Sxsru1q7/J0tTN6rPJUmVkaXO437/J08O8r8i6tcTKysqxvOC/2KOsu7m/ydLUzai5/bHgs8zK1rbOvPXH4byvyLrW0E1hc3Rlcr3atePW0LbBstnX97XE0bnBpqGjsb692sTayN2jrM7Sw8fP8rbB1d+96cnc1eK49lJlZGlzIFNlbnRpbmVstcS88rWlyrnTw6GjPC9wPgo8aDQgaWQ9"3-1基本配置">3-1、基本配置

由于Redis Sentinel是Redis原生支持的,以Redis Version 3.2為例,在下載安裝后就可以直接使用命令“redis-sentinel”啟動Sentinel了。Sentinel的主配置文件模板存放在Redis安裝目錄的下,默認名為“sentinel.conf”。以下命令可以啟動Sentinel(啟動Sentinel所依據的配置文件是一定要攜帶的參數):

# redis-sentinel ./sentinel.conf

Redis Sentinel本身也支持集群部署,而且為了在生產環境下避免Sentinel單點故障,所以也建議同時部署多個Sentinel節點。部署多個Sentinel還有一個原因,就是提高Master——Slave切換的準確性。以下的配置文件介紹會說明這一點。

下面我們介紹一些Sentinel主配置文件中的關鍵配置,注意Sentinel主配置文件也有類似Redis主配置文件提供的訪問保護模式(protected-mode)、訪問者權限控制(auth-pass)等,但是它們的意義基本上類似前文介紹過的,在Redis主配置文件中的相似內容,所以這里就不再贅述了。

sentinel monitor <master-name> <ip> <redis-port> <quorum>:

這個屬性是Redis Sentinel中的最主要設置元素,換句話說如果要開啟Sentinel甚至可以只設置這個屬性。它包括了四個參數:master-name,這個參數是一個英文名說明了Sentinel服務監聽的Master節點的別名,如果一個Sentinel服務需要同時監控多個Master,這需要設置多個不同的master-name;ip和redis-port,指向sentinel需要監控的Redis集群最初的那個Master節點(為什么會是最初呢?后文會說明)的ip和端口;quorum,投票數量這個參數很重要,如果是Sentinel集群方式下,它設定“當quorum個Sentinel認為Master異常了,就判定該Master真的異常了”。單個Sentinel節點認為Master下線了被稱為主管下線,而quorum個Sentinel節點都認為Master下線的情況被稱為客觀下線。

sentinel parallel-syncs <master-name> <numslaves>:

一旦原來的Master節點被認為客觀下線了,Sentinel就會啟動切換過程。大致來講就是從當前所有Slave節點選擇一個節點成為新的Master節點(這時在Redis中設定的slave-priority參數就會起作用了)。而其它的Slave其slaveof的Master信息將被sentinel切換到新的Master上。而一次同時并行切換多少個Slave到新的Master上就是這個參數決定的。如果整個Redis高可用集群的節點數量不多(沒有超過6個),建議使用默認值就可以了。

主配置文件中被rewrite的參數內容:sentinel.conf文件中的配置內容會隨著Sentinel的監控情況發生變化——由Sentinel程序動態寫入到文件中。例如sentinel known-slave參數、sentinel current-epoch參數和sentinel leader-epoch參數。

注意,在Sentinel中您只需要配置最初的Master的監控位置,無需配置Master下任何Slave的位置,Sentinel會自己識別到這些Master直接的或者間接的Slave。

3-2、切換效果

介紹完配置后,我們來簡單看一個Sentinel工作和切換的例子。這個例子中的有一個Master節點和一個Slave節點,當Master節點出現故障時,通過Sentinel監控到異常情況并自動完成Slave狀態的切換。

首先請保證您的Master節點和Slave節點都是正常工作的,這個過程可以參見筆者之前文章的介紹:

節點地址 節點作用
192.168.61.140 Redis Master
192.168.61.145 Redis Slave
192.168.61.140 Redis Sentinel

這里就不再贅述Redis Master和Redis Slave的內容了,因為在本文第2節中已經詳細介紹過。實際上您只需要打開Slave節點的主配置文件,并增加slaveof的配置信息,將其指向Master的IP和端口就可以了。以下是Sentinel節點主要更改的配置信息:

......
sentinel monitor mymaster 192.168.61.140 6379 1
......

由于在測試環境中我們只使用了一個Sentinel節點,所以設置sentinel monitor配置項中的quorum為1就可以了,代表有一個Sentinel節點認為Master不可用了,就開啟故障轉移過程。當然生產環境下不建議這樣使用。

之后我們使用以下Sentinel的主要配置信息啟動Sentinel:

# redis-sentinel ./sentinel.conf

......
8576:X 19 Dec 00:49:01.085 # Sentinel ID is 5a5eb7b97de060e7ad5f6aa20475a40b3d9fd3e1
8576:X 19 Dec 00:49:01.085 # +monitor master mymaster 192.168.61.140 6379 quorum 1
......

之后主動終止原來Master的運行過程(您可以直接使用kill命令,或者拔掉網線,又索性直接關機),來觀察Slave節點和Sentinel節點的日志情況:

當斷開原來的Master節點后,Slave節點將提示連接失效并開始重試。當Sentinel開始進入故障轉移并完成后,Salve又會打印相應的過程信息:

......
8177:S 19 Dec 00:53:17.467 * Connecting to MASTER 192.168.61.140:6379
8177:S 19 Dec 00:53:17.468 * MASTER <-> SLAVE sync started
8177:S 19 Dec 00:53:17.468 # Error condition on socket for SYNC: Connection refused
......
8177:M 19 Dec 00:53:18.134 * Discarding previously cached master state.
8177:M 19 Dec 00:53:18.134 * MASTER MODE enabled (user request from 'id=3 addr=192.168.61.140:51827 fd=5 name=sentinel-5a5eb7b9-cmd age=258 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=0 qbuf-free=32768 obl=36 oll=0 omem=0 events=r cmd=exec')
8177:M 19 Dec 00:53:18.138 # CONFIG REWRITE executed with success.
......

從以上Slave節點的內容可以看到,Slave被切換成了Master狀態。那么Sentinel本身有哪些重要的日志信息呢?如下所示:

......
// 當前Sentinel節點確定原Master主觀下線
8576:X 19 Dec 00:53:18.074 # +sdown master mymaster 192.168.61.140 6379
// 由于設置的quorum為1,所以一個Sentinel節點的主管下線就認為Master客觀下線了
8576:X 19 Dec 00:53:18.074 # +odown master mymaster 192.168.61.140 6379 #quorum 1/1
// 第三代,每轉移一次故障epoch的值+1,
// 不好意思,在書寫測試實例前,本人已經自行測試了兩次故障轉移,所以這里看到的epoch為3
// 這個信息會自動寫入到Sentinel節點的主配置文件中
8576:X 19 Dec 00:53:18.074 # +new-epoch 3
// 開始進行故障轉移
8576:X 19 Dec 00:53:18.074 # +try-failover master mymaster 192.168.61.140 6379

// 選舉出主導故障轉移的Sentinel節點,因為不是所有Sentinel節點都會主導這個過程
8576:X 19 Dec 00:53:18.084 # +vote-for-leader 5a5eb7b97de060e7ad5f6aa20475a40b3d9fd3e1 3
8576:X 19 Dec 00:53:18.084 # +elected-leader master mymaster 192.168.61.140 6379
8576:X 19 Dec 00:53:18.084 # +failover-state-select-slave master mymaster 192.168.61.140 6379

// 選擇提升哪一個slave作為新的master
8576:X 19 Dec 00:53:18.156 # +selected-slave slave 192.168.61.145:6379 192.168.61.145 6379 @ mymaster 192.168.61.140 6379
8576:X 19 Dec 00:53:18.156 * +failover-state-send-slaveof-noone slave 192.168.61.145:6379 192.168.61.145 6379 @ mymaster 192.168.61.140 6379
8576:X 19 Dec 00:53:18.211 * +failover-state-wait-promotion slave 192.168.61.145:6379 192.168.61.145 6379 @ mymaster 192.168.61.140 6379

// 提升原來的slave
8576:X 19 Dec 00:53:19.201 # +promoted-slave slave 192.168.61.145:6379 192.168.61.145 6379 @ mymaster 192.168.61.140 6379
// 試圖重寫所有salves節點的配置信息,并讓它們指向新的master
8576:X 19 Dec 00:53:19.201 # +failover-state-reconf-slaves master mymaster 192.168.61.140 6379
// 故障轉移結束
8576:X 19 Dec 00:53:19.250 # +failover-end master mymaster 192.168.61.140 6379
// 最終完成master節點的切換
8576:X 19 Dec 00:53:19.250 # +switch-master mymaster 192.168.61.140 6379 192.168.61.145 6379

// 注意原有的master節點會再顯示一條作為主觀下線,但是這次下線信息是以salve身份通知的
// 這是因為這次故障切換后,原來的master就算再上線,也只會作為Slave節點了
8576:X 19 Dec 00:53:19.251 * +slave slave 192.168.61.140:6379 192.168.61.140 6379 @ mymaster 192.168.61.145 6379
8576:X 19 Dec 00:53:49.305 # +sdown slave 192.168.61.140:6379 192.168.61.140 6379 @ mymaster 192.168.61.145 6379
......

通過Slave節點和Sentinel節點的日志可以看到,在經過了短暫的時間后Sentinel成功將唯一一個Slave節點轉換成了Master節點,并繼續向外部提供服務。

之后我們重新啟動原有Master節點,看看會發生什么:

# 以下是原有Master啟動后,在Sentinel顯示的信息

......
8576:X 19 Dec 01:31:12.743 * +reboot slave 192.168.61.140:6379 192.168.61.140 6379 @ mymaster 192.168.61.145 6379
8576:X 19 Dec 01:31:12.805 # -sdown slave 192.168.61.140:6379 192.168.61.140 6379 @ mymaster 192.168.61.145 6379
......

一個非常重要的現象是,當原來的Master節點再次啟動時,即使配置文件中沒有設定slaveof信息,它也會在Sentinel的協調下稱為Slave節點。這是因為任何一次Master到Slave的切換都是要付出代價的,其中除了狀態本身的判斷外,還有Sentinel自身協調和選舉過程(選舉哪一個Sentinel進行實質的切換動作),還有新的Master的選定問題,甚至包括Slave的slaveof目標變化過程中需要處理的數據一致性問題等等工作。所以最好的辦法就是:只要能夠保證Redis高可用集群持續工作,就不進行Master狀態的切換。

3-3、Java客戶端配合Sentinel的使用

通過Sentinel組建的高可用集群對比通過第三方軟件組建的高可用集群而言,有其明顯的優點。例如可以實時返回集群中每個Redis節點的狀態,且各節點間更能保持最佳的數據一致性,另外還可以在必要的時候通過轉移客戶端讀操作,減輕Master節點的工作壓力。但是它也有一個很明顯的缺點,就是由于整個集群可以向調用者開放多個Redis節點的地址,且Sentinel本身并不能充當路由器的作用,所以當Redis高可用集群進行狀態切換時,客戶端可能并不清楚原有的Master節點已經失效了。如下圖所示:

這里寫圖片描述

還好的是,Java最常用的Redis客戶端jedis提供了一組針對Sentinel的集群工具,讓客戶端可以在獲取當前Redis高可用集群中的Master節點后,再在這個Master節點上完成數據讀寫操作。但另外一個讀操作的負載問題還是沒有被解決,所有的讀操作也只會在Master節點完成。

這里寫圖片描述

我們來看看一些關鍵代碼:

......
// 這是基本的連接配置
// 當讓這些屬性都可以根據您的實際情況進行更改
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(100); 
poolConfig.setMaxIdle(50); 
poolConfig.setMinIdle(20); 
poolConfig.setMaxWaitMillis(6 * 1000); 
poolConfig.setTestOnBorrow(true);

// 以下是可用的多個Sentinel節點的ip和端口
Set<String> jedisClusterNodes = new HashSet<String>();
jedisClusterNodes.add('192.168.61.140:26379');
//如果有多個Sentinel一個一個添加進去
//jedisClusterNodes.add('192.168.61.139:26379');
JedisSentinelPool jedisSentinelPool = new JedisSentinelPool('mymaster', jedisClusterNodes , poolConfig);

// 開始插入信息
for(Integer index = 0 ; index < 10000 ; index++) {
    // 獲取最新的master信息
    Jedis master = null;
    try {
        master = jedisSentinelPool.getResource();
    } catch(JedisConnectionException e) {
        // 如果出現異常,說明當前的maser斷開了連接,那么等待一段時間后重試
        LOGGER.info('master is loss , waiting for try again......');
        synchronized (MasterSlaveApp.class) {
            MasterSlaveApp.class.wait(5000);
        }
        index--;
        continue;
    }

    // 開始正式插入
    master.set(('key' + index).getBytes(), index.toString().getBytes());
    LOGGER.info('write : ' + 'key' + index);
    synchronized (MasterSlaveApp.class) {
        // 停止0.5秒,以便觀察現象
        MasterSlaveApp.class.wait(500);
    }
}
jedisSentinelPool.close();
......

在示例代碼中Sentinel節點只有一個,存在于192.168.61.140:26379上。如果是生產環境建議不要對Sentinel進行單點部署,否則一旦Sentinel單點崩潰會造成整個Redis高可用集群在客戶端無法進行Master節點的切換。在初始階段192.168.61.140:6379是master節點,然后我們在程序執行過程中將原有的master節點關閉,這時上面的客戶端代碼片段可能的輸出以下日志信息(部分):

......
14639 [main] INFO redis_test.test.MasterSlaveApp  - write : key29
16144 [main] INFO redis_test.test.MasterSlaveApp  - master is loss , waiting for try again......
22148 [main] INFO redis_test.test.MasterSlaveApp  - master is loss , waiting for try again......
28151 [main] INFO redis_test.test.MasterSlaveApp  - master is loss , waiting for try again......
34155 [main] INFO redis_test.test.MasterSlaveApp  - master is loss , waiting for try again......
40159 [main] INFO redis_test.test.MasterSlaveApp  - master is loss , waiting for try again......
十二月 20, 2016 4:12:22 下午 redis.clients.jedis.JedisSentinelPool initPool
信息: Created JedisPool to master at 192.168.61.145:6379
46163 [main] INFO redis_test.test.MasterSlaveApp  - master is loss , waiting for try again......
51166 [main] INFO redis_test.test.MasterSlaveApp  - write : key30
51670 [main] INFO redis_test.test.MasterSlaveApp  - write : key31
......

==================================
后文內容預告:
4、Redis負載均衡方案
4-1、Twitter Twemproxy
4-2、Twemproxy存在的問題
5、Redis 3.X Cluster
5-1、Redis Cluster 簡介
5-2、Redis Cluster 搭建實例
5-3、Client連接到Redis Cluster

==========各位讀者對文章邏輯如果有質疑,歡迎在評論區留言。感謝幫助我指出錯誤的讀者。

Tag標簽: 集群   架構   方案  
  • 專題推薦

About IT165 - 廣告服務 - 隱私聲明 - 版權申明 - 免責條款 - 網站地圖 - 網友投稿 - 聯系方式
本站內容來自于互聯網,僅供用于網絡技術學習,學習中請遵循相關法律法規
亿游彩票平台 bj7| vrf| h7l| zjn| 7nv| jf7| xjp| j8x| rlj| 6nn| zb6| fz6| vpd| b6x| lxv| 6vd| nz7| xrp| f7v| jxv| 7ll| np5| xtj| v5n| rtb| xxv| 6xn| vrx| 6xd| df6| fjp| v6t| vfl| 4zp| lh4| jlj| x5f| htt| zbb| 5dj| nj5| xhn| f5l| zlb| 3vr| jd4| dnf| p4v| pjz| 4vf| nf4| dp4| xbr| r4f| pbr| d5j| vfd| 3vj| nx3| zlp| r3h| jtr| 3br| zb4| ht4| vlt| t4h| hbl| 2fv| vh2| xjr| x2l| ptr| 3ft| fj3| nfl| v3x| b3b| jfd| 3lj| pj1| nxn| v1b| ztv| d2t| ztt| 2jr| np2| dhf| d2t|