這篇文章主要為大家展示了“redis中sentinel故障轉(zhuǎn)移的示例分析”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Redis中sentinel故障轉(zhuǎn)移的示例分析”這篇文章吧。
成都創(chuàng)新互聯(lián)公司是一家專注于做網(wǎng)站、成都做網(wǎng)站和移動服務(wù)器托管的網(wǎng)絡(luò)公司,有著豐富的建站經(jīng)驗和案例。
當(dāng)兩臺以上的Redis實例形成了主備關(guān)系,它們組成的集群就具備了一定的高可用性:當(dāng)master發(fā)生故障的時候,slave可以成為新的master對外提供讀寫服務(wù),這種運營機制成為failover。
那么誰來發(fā)現(xiàn)master的故障做failover決策?
一種方式是,保持一個daemo進程,監(jiān)控著所有的master-slave節(jié)點,如下圖所示:
一個Redis集群里面有一個master和兩個slave,這個daemon進程監(jiān)控著這三個節(jié)點。但daemon為單節(jié)點,本身可用性無法保證。需要引入多daemon,如下圖所示:
多個daemon解決了可用性問題,但又出現(xiàn)了一致性問題,如何就某個master是否可用達成一致?例如上圖兩個daemon1和和master網(wǎng)絡(luò)不通,daemon和master連接暢通,那此時mater節(jié)點是否需要failover那?
Redis的sentinel提供了一套多daemon間的交互機制,多個daemon間組成一個集群,成為sentinel集群,daemon節(jié)點也稱為sentinel節(jié)點。如下圖所示:
這些節(jié)點相互間通信、選舉、協(xié)商,在master節(jié)點的故障發(fā)現(xiàn)、failover決策上表現(xiàn)出一致性。
sentinel集群監(jiān)視任意多個master以及master下的slave,自動將下線的master從其下的某個slave升級為新的master代替繼續(xù)處理命令請求。
啟動一個Sentinel可以使用命令:
./redis-sentinel ../sentinel.conf
或者命令:
./redis-server ../sentinel.conf --sentinel
當(dāng)一個Sentinel啟動時,它需要執(zhí)行以下步驟:
初始化服務(wù)器
Sentinel本質(zhì)上是運行在特殊模式下的Redis服務(wù)器,它和普通的Redis服務(wù)器執(zhí)行的工作不同,初始化過程也不完全相同。如普通的Redis服務(wù)器初始化會載入RDB或者AOF文件來恢復(fù)數(shù)據(jù),而Sentinel啟動時不會載入,因為Sentinel并不使用數(shù)據(jù)庫。
將普通Redis服務(wù)器使用的代碼替換成Sentinel專用代碼
將一部分普通Redis服務(wù)器使用的代碼替換成Sentinel專用代碼。如普通Redis服務(wù)器使用server.c/redisCommandTable作為服務(wù)器的命令表:
truct redisCommand redisCommandTable[] = { {"module",moduleCommand,-2,"as",0,NULL,0,0,0,0,0}, {"get",getCommand,2,"rF",0,NULL,1,1,1,0,0}, {"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0}, {"setnx",setnxCommand,3,"wmF",0,NULL,1,1,1,0,0}, {"setex",setexCommand,4,"wm",0,NULL,1,1,1,0,0}, {"psetex",psetexCommand,4,"wm",0,NULL,1,1,1,0,0}, {"append",appendCommand,3,"wm",0,NULL,1,1,1,0,0}, ..... {"del",delCommand,-2,"w",0,NULL,1,-1,1,0,0}, {"unlink",unlinkCommand,-2,"wF",0,NULL,1,-1,1,0,0}, {"exists",existsCommand,-2,"rF",0,NULL,1,-1,1,0,0}, {"setbit",setbitCommand,4,"wm",0,NULL,1,1,1,0,0}, {"getbit",getbitCommand,3,"rF",0,NULL,1,1,1,0,0}, {"bitfield",bitfieldCommand,-2,"wm",0,NULL,1,1,1,0,0}, {"setrange",setrangeCommand,4,"wm",0,NULL,1,1,1,0,0}, {"getrange",getrangeCommand,4,"r",0,NULL,1,1,1,0,0}, {"substr",getrangeCommand,4,"r",0,NULL,1,1,1,0,0}, {"incr",incrCommand,2,"wmF",0,NULL,1,1,1,0,0}, {"decr",decrCommand,2,"wmF",0,NULL,1,1,1,0,0}, {"mget",mgetCommand,-2,"rF",0,NULL,1,-1,1,0,0}, {"rpush",rpushCommand,-3,"wmF",0,NULL,1,1,1,0,0}, {"lpush",lpushCommand,-3,"wmF",0,NULL,1,1,1,0,0} ...... }
Sentinel使用sentinel.c/sentinelcmds作為服務(wù)器列表,如下所示:
struct redisCommand sentinelcmds[] = { {"ping",pingCommand,1,"",0,NULL,0,0,0,0,0}, {"sentinel",sentinelCommand,-2,"",0,NULL,0,0,0,0,0}, {"subscribe",subscribeCommand,-2,"",0,NULL,0,0,0,0,0}, {"unsubscribe",unsubscribeCommand,-1,"",0,NULL,0,0,0,0,0}, {"psubscribe",psubscribeCommand,-2,"",0,NULL,0,0,0,0,0}, {"punsubscribe",punsubscribeCommand,-1,"",0,NULL,0,0,0,0,0}, {"publish",sentinelPublishCommand,3,"",0,NULL,0,0,0,0,0}, {"info",sentinelInfoCommand,-1,"",0,NULL,0,0,0,0,0}, {"role",sentinelRoleCommand,1,"l",0,NULL,0,0,0,0,0}, {"client",clientCommand,-2,"rs",0,NULL,0,0,0,0,0}, {"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0}, {"auth",authCommand,2,"sltF",0,NULL,0,0,0,0,0} }
初始化Sentinel狀態(tài)
服務(wù)器會初始化一個sentinel.c/sentinelState結(jié)構(gòu)(保存服務(wù)器中所有和Sentinel功能有關(guān)的狀態(tài))。
struct sentinelState { char myid[CONFIG_RUN_ID_SIZE+1]; /* This sentinel ID. */ //當(dāng)前紀元,用于實現(xiàn)故障轉(zhuǎn)移 uint64_t current_epoch; /* Current epoch. */ //監(jiān)視的主服務(wù)器 //字典的鍵是主服務(wù)器的名字 //字典的值則是一個指向sentinelRedisInstances結(jié)構(gòu)的指針 dict *masters; /* Dictionary of master sentinelRedisInstances. Key is the instance name, value is the sentinelRedisInstance structure pointer. */ //是否進入tilt模式 int tilt; /* Are we in TILT mode? */ //目前正在執(zhí)行的腳本數(shù)量 int running_scripts; /* Number of scripts in execution right now. */ //進入tilt模式的時間 mstime_t tilt_start_time; /* When TITL started. */ //最后一次執(zhí)行時間處理器的時間 mstime_t previous_time; /* Last time we ran the time handler. */ // 一個FIFO隊列,包含了所有需要執(zhí)行的用戶腳本 list *scripts_queue; /* Queue of user scripts to execute. */ char *announce_ip; /* IP addr that is gossiped to other sentinels if not NULL. */ int announce_port; /* Port that is gossiped to other sentinels if non zero. */ unsigned long simfailure_flags; /* Failures simulation. */ int deny_scripts_reconfig; /* Allow SENTINEL SET ... to change script paths at runtime? */ }
根據(jù)給定的配置文件,初始化Sentinel的監(jiān)視主服務(wù)器列表
對Sentinel狀態(tài)的初始化將引發(fā)對masters字典的初始化,而master字典的初始化是根據(jù)被載入的Sentinel配置文件來進行的。
字典的key是監(jiān)視主服務(wù)器的名字,字典的值則是被監(jiān)控主服務(wù)器對應(yīng)的sentinel.c/sentinelRedisInstance結(jié)構(gòu)。
sentinelRedisInstance結(jié)構(gòu)部分屬性如下:
typedef struct sentinelRedisInstance { //標識值,記錄了實例的類型,以及該實例的當(dāng)前狀態(tài) int flags; /* See SRI_... defines */ //實例的名字 //主服務(wù)器的名字由用戶在配置文件中設(shè)置 //從服務(wù)器以及Sentinel的名字由Sentinel自動設(shè)置 //格式為ip:port,例如“127.0.0.1:26379” char *name; /* Master name from the point of view of this sentinel. */ //實例運行的ID char *runid; /* Run ID of this instance, or unique ID if is a Sentinel.*/ //配置紀元,用于實現(xiàn)故障轉(zhuǎn)移 uint64_t config_epoch; /* Configuration epoch. */ //實例的地址 sentinelAddr *addr; /* Master host. */ //sentinel down-after-milliseconds選項設(shè)定的值 //實例無響應(yīng)多少毫秒之后才會被判斷為主觀下線(subjectively down) mstime_t down_after_period; /* Consider it down after that period. */ //sentinel monitor <master-name> <ip> <redis-port> <quorum>選項中的quorum //判斷這個實例為客觀下線(objective down)所需的支持投票的數(shù)量 unsigned int quorum;/* Number of sentinels that need to agree on failure. */ //sentinel parallel-syncs <master-name> <numreplicas> 選項的numreplicas值 //在執(zhí)行故障轉(zhuǎn)移操作時,可以同時對新的主服務(wù)器進行同步的從服務(wù)器數(shù)量 int parallel_syncs; /* How many slaves to reconfigure at same time. */ //sentinel failover-timeout <master-name> <milliseconds>選項的值 //刷新故障遷移狀態(tài)的最大時限 mstime_t failover_timeout; /* Max time to refresh failover state. */ }
例如啟動Sentinel時,配置了如下的配置文件:
# sentinel monitor <master-name> <ip> <redis-port> <quorum> sentinel monitor master1 127.0.0.1 6379 2 # sentinel down-after-milliseconds <master-name> <milliseconds> sentinel down-after-milliseconds master1 30000 # sentinel parallel-syncs <master-name> <numreplicas> sentinel parallel-syncs master1 1 # sentinel failover-timeout <master-name> <milliseconds> sentinel failover-timeout master1 900000
則Sentinel則會為主服務(wù)器master1創(chuàng)建如下圖所示的實例結(jié)構(gòu):
Sentinel狀態(tài)以及masters字典的機構(gòu)如下:
創(chuàng)建連向主服務(wù)器的網(wǎng)絡(luò)連接
創(chuàng)建連向被監(jiān)視主服務(wù)器的網(wǎng)絡(luò)連接,Sentinel將成為主服務(wù)器的客戶端,向主服務(wù)器發(fā)送命令并從命令回復(fù)獲取信息。
Sentinel會創(chuàng)建兩個連向主服務(wù)器的異步網(wǎng)絡(luò)連接:
命令連接,用于向主服務(wù)器發(fā)送命令并接收命令回復(fù)
訂閱連接,訂閱主服務(wù)器的_sentinel_:hello頻道
Sentinel默認會以每十秒一次的頻率,通過命令連接向被監(jiān)視的master和slave發(fā)送INFO命令。
通過master的回復(fù)可獲取master本身信息,包括run_id域記錄的服務(wù)器運行ID,以及role域記錄的服務(wù)器角色。另外還會獲取到master下的所有的從服務(wù)器信息,包括slave的ip地址和port端口號。Sentinel無需用戶提供從服務(wù)器的地址信息,由master返回的slave的ip地址和port端口號,可以自動發(fā)現(xiàn)slave。
當(dāng)Sentinel發(fā)現(xiàn)master有新的slave出現(xiàn)時,Sentinel會為這個新的slave創(chuàng)建相應(yīng)的實例外,Sentinel還會創(chuàng)建到slave的命令連接和訂閱連接。
根據(jù)slave的INFO命令的回復(fù),Sentinel會提取如下信息:
1.slave的運行ID run_id
2.slave的角色role
3.master的ip地址和port端口
4.master和slave的連接狀態(tài)master_link_status
5.slave的優(yōu)先級slave_priority
6.slave的復(fù)制偏移量slave_repl_offset
Sentinel在默認情況下會以每兩秒一次的頻率,通過命令連接向所有被監(jiān)視的master和slave的_sentinel_:hello頻道發(fā)送一條信息
發(fā)送以下格式的命令:
PUBLISH _sentinel_:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"
以上命令相關(guān)參數(shù)意義:
參數(shù) | 意義 |
---|---|
s_ip | Sentinel的ip地址 |
s_port | Sentinel的端口號 |
s_runid | Sentinel的運行ID |
s_runid | Sentinel的運行ID |
m_name | 主服務(wù)器的名字 |
m_ip | 主服務(wù)器的IP地址 |
m_port | 主服務(wù)器的端口號 |
m_epoch | 主服務(wù)器當(dāng)前的配置紀元 |
Sentinel與master或者slave建立訂閱連接之后,Sentinel就會通過訂閱連接發(fā)送對_sentinel_:hello頻道的訂閱,訂閱會持續(xù)到Sentinel與服務(wù)器的連接斷開為止
命令如下所示:
SUBSCRIBE sentinel:hello
如上圖所示,對于每個與Sentinel連接的服務(wù)器 ,Sentinel既可以通過命令連接向服務(wù)器頻道_sentinel_:hello頻道發(fā)送信息,又通過訂閱連接從服務(wù)器的_sentinel_:hello頻道接收信息。
sentinel間會相互感知,新加入的sentinel會向master的_sentinel_:hello頻道發(fā)布一條消息,包括自己的消息,其它該頻道訂閱者sentinel會發(fā)現(xiàn)新的sentinel。隨后新的sentinel和其它sentinel會創(chuàng)建長連接。
相互連接的各個Sentinel可以進行信息交換。Sentinel為master創(chuàng)建的實例結(jié)構(gòu)中的sentinels字典保存了除Sentinel本身之外,所有同樣監(jiān)視這個主服務(wù)器的其它Sentinel信息。
前面也講到sentinel會為slave創(chuàng)建實例(在master實例的slaves字典中)。現(xiàn)在我們也知道通過sentinel相互信息交換,也創(chuàng)建了其它sentinel的實例(在master實例的sentinels字典中)。我們將一個sentinel中保存的實例結(jié)構(gòu)大概情況理一下,如下圖所示:
從上圖可以看到slave和sentinel字典的鍵由其ip地址和port端口組成,格式為ip:port,其字典的值為其對應(yīng)的sentinelRedisInstance實例。
主觀不可用
默認情況下Sentinel會以每秒一次的頻率向所有與它創(chuàng)建了命令連接的master(包括master、slave、其它Sentinel)發(fā)送PING命令,并通過實例返回的PING命令回復(fù)來判斷實例是否在線。
PING命令回復(fù)分為下面兩種情況:
有效回復(fù):實例返回 +PONG、-LOADING、-MASTERDOWN三種回復(fù)的一種
無效回復(fù):除上面有效回復(fù)外的其它回復(fù)或者在指定時限內(nèi)沒有任何返回
Sentinel配置文件中的設(shè)置down-after-milliseconds毫秒時效內(nèi)(各個sentinel可能配置的不相同),連續(xù)向Sentinel返回?zé)o效回復(fù),那么sentinel將此實例置為主觀下線狀態(tài),在sentinel中維護的該實例flags屬性中打開SRI_S_DOWN標識,例如master如下所示:
客觀不可用
在sentinel發(fā)現(xiàn)主觀不可用狀態(tài)后,它會將“主觀不可用狀態(tài)”發(fā)給其它sentinel進行確認,當(dāng)確認的sentinel節(jié)點數(shù)>=quorum,則判定該master為客觀不可用,隨后進入failover流程。
上面說到將主觀不可用狀態(tài)發(fā)給其它sentinel使用如下命令:
SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <runid>
各個參數(shù)的意義如下:
ip:被sentinel判斷為主觀下線的主服務(wù)器的ip地址
port: 被sentinel判斷為主觀下線的主服務(wù)器的port地址
current_epoch:sentinel的配置紀元,用于選舉領(lǐng)頭Sentinel
runid:可以為*號或者Sentinel的運行ID,*號代表檢測主服務(wù)器客觀下線狀態(tài)。Sentinel的運行ID用于選舉領(lǐng)頭Sentinel
接受到以上命令的sentinel會反回一條包含三個參數(shù)的Multi Bulk回復(fù):
1)<down_state> 目標sentinel對該master檢查結(jié)果,1:master已下線 2:master未下線
2)<leader_runid> 兩種情況,*表示僅用于檢測master下線狀態(tài) ,否則表示局部領(lǐng)頭Sentinel的運行ID(選舉領(lǐng)頭Sentinel)
3)<leader_epoch> 當(dāng)leader_runid為時,leader_epoch始終為0。不為時則表示目標Sentinel的局部領(lǐng)頭Sentinel的配置紀元(用于選舉領(lǐng)頭Sentinel)
其中節(jié)點數(shù)量限制quorum為sentinel配置文件中配置的
sentinel monitor <master-name> <ip> <redis-port> <quorum>
quorum選項,不同的sentinel配置的可能不相同。
當(dāng)sentinel認為master為客觀下線狀態(tài),則會將master屬性中的flags的SRI_O_DOWN標識打開,例如master如下圖所示:
當(dāng)一臺master宕機時,可能多個sentinel節(jié)點同時發(fā)現(xiàn)并通過交互確認相互的“主觀不可用狀態(tài)”,同時達到“客觀不可用狀態(tài)”,同時打算發(fā)起failover。但最終只能有一個sentinel節(jié)點作為failover發(fā)起者,那么就需要選舉出Sentinel Leader,需要開始一個Sentinel Leader選舉過程。
Redis的Sentinel機制采用類似于Raft協(xié)議實現(xiàn)這個選舉算法:
1.sentinelState的epoch變量類似于raft協(xié)議中的term(選舉回合)。
2.每一個確認了master“客觀不可用”的sentinel節(jié)點都會向周圍廣播自己的參選請求(SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <run_id> ,current_epoch為自己的配置紀元,run_id為自己的運行ID)
3.每一個接收到參選請求的sentinel節(jié)點如果還沒接收到其它參選請求,它就將本回合的意向置為首個參選sentinel并回復(fù)它(先到先得);如果已經(jīng)在本回合表過意向了,則拒絕其它參選,并將已有意向回復(fù)(如上所介紹的三個參數(shù)的Multi Bulk回復(fù),down_state為1,leader_runid為首次接收到的發(fā)起參選請求的源sentinel的運行ID,leader_epoch為首次接收到的發(fā)起參選請求的源sentinel的配置紀元)
4.每個發(fā)起參選請求的sentinel節(jié)點如果收到超過一半的意向同意某個參選sentinel(可能是自己),則確定該sentinel為leader。如果本回合持續(xù)了足夠長時間未選出leader,則開啟下一個回合
leader sentinel 確定之后,leader sentinel從master所有的slave中依據(jù)一定規(guī)則選取一個作為新的master。
在選舉出Sentinel Leader之后,sentinel leader對已下線master執(zhí)行故障轉(zhuǎn)移:
sentinel leader對已下線的master的所有slave中,選出一個狀態(tài)良好、數(shù)據(jù)完整的slave,然后向這個slave發(fā)送:SLAVEOF no one命令,將這個slave轉(zhuǎn)換為master。
我們來看下新的master是怎么挑選出來的?Sentinel leader會將已下線的所有slave保存到一個列表,然后按照以下規(guī)則過濾篩選:
優(yōu)先級最高的slave,redis.conf配置中replica-priority選項來標識,默認為100,replica-priority較低的優(yōu)先級越高。0為特殊優(yōu)先級,標志為不能升級為master。
如果存在多個優(yōu)先級相等的slave,則會選擇復(fù)制偏移量(offset)最大的slave(數(shù)據(jù)更加完整)
如果存在多個優(yōu)先級相等,最大復(fù)制偏移量最大的slave,則選擇運行ID最小的slave
選出需要升級為新的master的slave后,Sentinel Leader會向這個slave發(fā)送SLAVEOF no one 命令。之后Sentinel會以每秒一次頻率(平時是十秒一次)向被升級slave發(fā)送INFO,當(dāng)回復(fù)的role由slave變?yōu)閙aster時Sentinel Leader就會知道已升級為master。
sentinel leader 向已下線的master屬下的slave發(fā)送SLAVEOF命令(SLAVEOF <new_master_ip> <new_master_port>),去復(fù)制新的master。
將舊的master設(shè)置為新的master的slave,并繼續(xù)對其監(jiān)視,當(dāng)其重新上線時Sentinel會執(zhí)行命令讓其成為新的master的slave。
以上是“Redis中sentinel故障轉(zhuǎn)移的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
文章標題:Redis中sentinel故障轉(zhuǎn)移的示例分析
地址分享:http://aaarwkj.com/article8/jeggop.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)網(wǎng)站制作、云服務(wù)器、網(wǎng)站導(dǎo)航、小程序開發(fā)、移動網(wǎng)站建設(shè)、網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)