首頁  >  文章  >  資料庫  >  Redis進階學習高可用之哨兵(總結分享)

Redis進階學習高可用之哨兵(總結分享)

WBOY
WBOY轉載
2022-02-22 17:29:041952瀏覽

這篇文章為大家帶來了關於Redis中高可用哨兵的相關知識,其中包括了作用架構、部署以及配置的相關問題,希望對大家有幫助。

Redis進階學習高可用之哨兵(總結分享)

推薦學習:Redis影片教學

#一、作用與架構

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節點,不儲存資料。
  • 資料節點:主節點和從節點都是資料節點。

二、部署

這部分將部署一個簡單的哨兵系統,包含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

3.  总结

按照上述方式配置和启动之后,整个哨兵系统就启动完毕了。可以通过redis-cli连接哨兵节点进行验证

哨兵系统的搭建过程,有几点需要注意:

(1)哨兵系统中的主从节点,与普通的主从节点并没有什么区别,故障发现和转移是由哨兵来控制和完成的。

(2)哨兵节点本质上是redis节点。

(3)每个哨兵节点,只需要配置监控主节点,便可以自动发现其他的哨兵节点和从节点。

(4)在哨兵节点启动和故障转移阶段,各个节点的配置文件会被重写(config rewrite)。

三、客户端访问哨兵系统

上一小节演示了哨兵的两大作用:监控和自动故障转移,本小节则结合客户端演示哨兵的另外两个作用:配置提供者和通知。

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();
}

Jedis客户端对哨兵提供了很好的支持。如上述代码所示,我们只需要向Jedis提供哨兵节点集合和masterName,构造JedisSentinelPool对象;然后便可以像使用普通redis连接池一样来使用了:通过pool.getResource()获取连接,执行具体的命令。2.  客户端原理

在整个过程中,我们的代码不需要显式的指定主节点的地址,就可以连接到主节点;代码中对故障转移没有任何体现,就可以在哨兵完成故障转移后自动的切换主节点。之所以可以做到这一点,是因为在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

(2)通知:哨兵节点在故障转移完成后,会将新的主节点信息发送给客户端,以便客户端及时切换主节点。然后,将前述客户端代码在局域网的另外一台机器上运行,会发现客户端无法连接主节点;这是因为哨兵作为配置提供者,客户端通过它查询到主节点的地址为127.0.0.1:6379,客户端会向127.0.0.1:6379建立redis连接,自然无法连接。如果哨兵是代理,这个问题就不会出现了。

四、基本原理

前面介绍了哨兵部署、使用的基本方法,本部分介绍哨兵实现的基本原理。

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重新上線後,它會成為新的主節點的從節點。
五、設定與實作建議

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。

六、總結

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

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

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

推薦學習:《Redis影片教學》、《2022最新redis面試題大全及答案

以上是Redis進階學習高可用之哨兵(總結分享)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除