<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    jinfeng_wang

    G-G-S,D-D-U!

    BlogJava 首頁 新隨筆 聯系 聚合 管理
      400 Posts :: 0 Stories :: 296 Comments :: 0 Trackbacks
    http://weizijun.cn/2015/04/30/redis%20sentinel%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/ 



    Sentinel是Redis官方自帶的工具,中文意思是哨兵,顧名思義,就是守衛Redis的好幫手。NCR(網易云redis)準備開發高可用的Redis集群,有計劃使用Sentinel。本文接下來介紹下Sentinel的設計與實現。本文介紹的Sentinel是用的2.8.19版本(最新的3.0.0版本Sentinel的功能只比2.8.19多了一個client命令)。

    先引用官方的說法介紹下Sentinel的作用。Redis Sentinel是一個幫助管理Redis實例的系統,它提供以下功能:

    • 監控(Monitoring):Sentinel會不斷的檢查你的主節點和從節點是否正常工作。
    • 通知(Notification):被監控的Redis實例如果出現問題,Sentinel可以通過API(pub)通知系統管理員或者其他程序。
    • 自動故障轉移(Automatic failover):如果一個master離線,Sentinel會開始進行故障轉移,master下的一個slave會被選為新的master,其他的slave會開始復制新的master。應用可以通過Redis服務的通知機制更新新的master地址。
    • 配置提供者(Configuration provider):客戶端可以把Sentinel作為權威的配置發布者來獲得最新的master地址。如果發生了故障轉移,Sentinel集群會通知客戶端新的master地址。

    Sentinel是如何運行的?又是如何提供對Redis的監控和故障轉移呢?

    image

    特殊狀態的Redis

    一個Sentinel就是一個運行在特殊狀態下的Redis,以至于可以用啟動Redis Server的方式啟動Sentinel。不過Sentinel有自己的命令列表,它支持的命令不多。

    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},     {"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0} }; 

    上面就是Sentinel支持的所有命令了。后面會對這些命令進行詳細的說明。

    配置Redis主節點,自動發現從節點

    Sentinel運行起來后,會根據配置文件的"sentinel monitor <master-name> <ip> <redis-port> <quorum>"去連接master,這里我們把master-name下的所有節點看成一個group,一個group由一個master,若干個slave組成。Sentinel只需配置主節點的ip、port即可。Sentinel會通過向master發送INFO命令來獲取master下的slave,然后把slave加入group。

    自動發現監控同一個group的其他Sentinel

    Sentinel連接Redis節點,會創建兩條對節點的連接,一條用來向節點發送命令,另一條用來訂閱“__sentinel__:hello”頻道(hello頻道)發來的消息,這個頻道用做Gossip協議發現其他監控此Redis節點的Sentinel。每個Sentinel會定時向頻道發送消息,然后也會接收到其他Sentinel從hello頻道發來的消息,消息的格式如下:sentinel_ip,sentinel_port,sentinel_runid,current_epoch,master_name,master_ip,master_port,master_config_epoch(127.0.0.1,26381,99ce8dc79e55ce9de040b0cd13d152900db9a7e1,24,mymaster2,127.0.0.1,8000,0)。

    這樣監控同一個group的的Sentinel就組成了一個集群,每個Sentinel會創建一條連向其他Sentinel的連接,這是在做自動故障轉移的時候可以像其他節點發送命令。

    推送消息

    Sentinel在監控Redis和其他Sentinel的時候,發現的異常以及完成的操作都會通過Publish的方式推送出去,客戶端想了解Sentinel的處理結果,只要訂閱相應的消息類型即可。Sentinel使用的推送方式用的是Redis現有的Pub/Sub方式。Sentinel基本上會把它整個處理流程都推送出來,然而客戶端一般只要關注主從切換的消息即可。

    故障檢查

    Sentinel會跟group的每個節點以及監控同一個group的其他Sentinel保持心跳,Sentinel會定時向這些節點發送Ping命令,然后等待Pong命令回復。如果一段時間沒有收到Pong命令。Sentinel就會主觀的認為該節點離線。對于其他Sentinel和group內的slave掛了,Sentinel檢測到他們離線也不需要做什么事情,只是簡單的推送一條+sdown的消息。如果檢測到master離線,Sentinel就要確定是否需要進行主從切換。此時Sentinel會向其他Sentinel發送is-master-down-by-addr命令(命令格式:SENTINEL IS-master-DOWN-BY-ADDR <ip> <port> <current-epoch> <runid>),這個命令有2個功能,這時候的用法是用來向其他節點獲取master是否離線的信息。還有一個用法是用來選舉leader的,該命令會讓其他Sentinel給自己投票,已經投過票的Sentinel會返回投票的結果。Sentinel在監控group的時候會配置一個quorum,Sentinel接收到超過quorum個Sentinel認為master掛了(quorum包含自己),Sentinel就會認為該master是客觀下線了。接著Sentinel就進入了自動故障轉移狀態。

    Sentinel間投票選leader

    Sentinel認為master客觀下線了,就開始故障轉移流程,故障轉移的第一步就是競選leaderSentinel采用了Raft協議實現了Sentinel間選舉Leader的算法,不過也不完全跟論文描述的步驟一致。Sentinel集群運行過程中故障轉移完成,所有Sentinel又會恢復平等。Leader僅僅是故障轉移操作出現的角色。

    選舉流程

    Sentinel采用了Raft協議實現了Sentinel間選舉Leader的算法,不過也不完全跟論文描述的步驟一致。

    • 1、某個Sentinel認定master客觀下線的節點后,該Sentinel會先看看自己有沒有投過票,如果自己已經投過票給其他Sentinel了,在2倍故障轉移的超時時間自己就不會成為Leader。相當于它是一個Follower。
    • 2、如果該Sentinel還沒投過票,那么它就成為Candidate。
    • 3、和Raft協議描述的一樣,成為Candidate,Sentinel需要完成幾件事情
      • 1)更新故障轉移狀態為start
      • 2)當前epoch加1,相當于進入一個新term,在Sentinel中epoch就是Raft協議中的term。
      • 3)更新自己的超時時間為當前時間隨機加上一段時間,隨機時間為1s內的隨機毫秒數。
      • 4)向其他節點發送is-master-down-by-addr命令請求投票。命令會帶上自己的epoch。
      • 5)給自己投一票,在Sentinel中,投票的方式是把自己master結構體里的leader和leader_epoch改成投給的Sentinel和它的epoch。
    • 4、其他Sentinel會收到Candidate的is-master-down-by-addr命令。如果Sentinel當前epoch和Candidate傳給他的epoch一樣,說明他已經把自己master結構體里的leader和leader_epoch改成其他Candidate,相當于把票投給了其他Candidate。投過票給別的Sentinel后,在當前epoch內自己就只能成為Follower。
    • 5、Candidate會不斷的統計自己的票數,直到他發現認同他成為Leader的票數超過一半而且超過它配置的quorum(quorum可以參考《redis sentinel(哨兵) 設計與實現》)。Sentinel比Raft協議增加了quorum,這樣一個Sentinel能否當選Leader還取決于它配置的quorum。
    • 6、如果在一個選舉時間內,Candidate沒有獲得超過一半且超過它配置的quorum的票數,自己的這次選舉就失敗了。
    • 7、如果在一個epoch內,沒有一個Candidate獲得更多的票數。那么等待超過2倍故障轉移的超時時間后,Candidate增加epoch重新投票。
    • 8、如果某個Candidate獲得超過一半且超過它配置的quorum的票數,那么它就成為了Leader。
    • 9、與Raft協議不同,Leader并不會把自己成為Leader的消息發給其他Sentinel。其他Sentinel等待Leader從slave選出master后,檢測到新的master正常工作后,就會去掉客觀下線的標識,從而不需要進入故障轉移流程。

    關于Sentinel超時時間的說明

    Sentinel超時機制并不像Raft協議描述的那樣只使用了一個隨機超時機制。它有幾個超時概念。

    • failover_start_time 下一選舉啟動的時間。默認是當前時間加上1s內的隨機毫秒數
    • failover_state_change_time 故障轉移中狀態變更的時間。
    • failover_timeout 故障轉移超時時間。默認是3分鐘。
    • election_timeout 選舉超時時間,是默認選舉超時時間和failover_timeout的最小值。默認是10s。

    Follower成為Candidate后,會更新failover_start_time為當前時間加上1s內的隨機毫秒數。更新failover_state_change_time為當前時間。

    Candidate的當前時間減去failover_start_time大于election_timeout,說明Candidate還沒獲得足夠的選票,此次epoch的選舉已經超時,那么轉變成Follower。需要等到mstime() - failover_start_time < failover_timeout*2的時候才開始下一次獲得成為Candidate的機會。

    如果一個Follower把某個Candidate設為自己認為的Leader,那么它的failover_start_time會設置為當前時間加上1s內的隨機毫秒數。這樣它就進入了上面說的需要等到mstime() - failover_start_time < failover_timeout*2的時候才開始下一次獲得成為Candidate的機會。

    因為每個Sentinel判斷節點客觀下線的時間不是同時開始的,一般都有先后,這樣先開始的Sentinel就更有機會贏得更多選票,另外failover_state_change_time為1s內的隨機毫秒數,這樣也把各個節點的超時時間分散開來。本人嘗試過很多次,Sentinel間的Leader選舉過程基本上一個epoch內就完成了。

    故障轉移

    Sentinel一旦確定自己是leader后,就開始從slave中選出一個節點來作為master。以下是選擇slave的流程:

    • 1、過濾掉slave列表中主觀、客觀下線和離線的slave。
    • 2、過濾掉slave列表中5s沒響應ping的slave。
    • 3、過濾掉slave列表中優先級為0的slave。(slave的優先級是redis的配置參數slave-priority,默認是100)
    • 3、過濾掉slave列表中一段時間沒有回復INFO的slave。
    • 4、過濾掉slave列表中很久沒跟master連接的slave。
    • 5、比較篩選后的slave,優先級小的slave被選為新master。
    • 6、如果優先級相同,比較slave對原master的復制偏移量,偏移量大的slave被選為新master。
    • 7、如果復制偏移量相同,那就直接比較slave的運行id,字符串小的slave被選為新master。

    如果依據選擇流程沒有選出可用的slave,leader Sentinel會終止本次故障轉移。

    如果選擇除了可用的slave,那么leader Sentinel會給該slave發送slaveof no one命令,表示該slave不再復制其他節點,成為了master。然后leader Sentinel就會一直等待選出slave的INFO信息里面確認了自己的master身份。如果等待超時了,leader Sentinel只得終止本次故障轉移。

    如果選出的slave確認了自己的master身份,leader Sentinel會讓其他slave復制新的master,由于初次復制會帶來很大的IO開銷,Sentinel有個parallel_syncs參數,用來確定一次讓多少個slave復制新master。一個slave復制master如果超過10s,leader Sentinel會重新發送復制命令。如果在指定的故障轉移時間內還沒有完成全部的復制工作,leader Sentinel就會忽略那些沒復制的slave。leader Sentinel只是向這些slave發送一次復制命令,不等待他們復制成功就直接完成了全部故障轉移工作。

    全部故障轉移工作完成后,leader Sentinel就會推送+switch-master消息,同時重置master,重置操作會釋放掉原來master全部的slave對象和監聽該master的其他Sentinel對象,然后創建出新的slave對象。

    TILT保護模式

    TILT模式是Sentinel發現進程出現異常時候的一種保護模式。Sentinel定時器默認每100ms執行一次,Sentinel每次啟動定時任務的時候會檢查下上次定時任務執行的時間,如果超過2s或者小于0了,Sentinel就認為操作系統出現異常,導致一次任務的執行時間過長,就會進入TILT模式。TILT模式下,Sentinel不再執行任何操作。其他Sentinel發送的SENTINEL is-master-down-by-addr命令會直接返回負值。

    如果TILT模式下的Sentinel正常運行超過30s,Sentinel就會解除TILT模式。

    客戶端處理流程

    客戶端可以通過Sentinel獲得group的信息。官方給出了客戶端操作的推薦方式。看了下Jedis的實現,基本就是按照官方的操作流程進行的。首先客戶端配置監聽該group的全部Sentinel。連接第一個Sentinel,如果連不上就重新連接下一個,直到連上一個Sentinel。

    連上Sentinel后,發送SENTINEL get-master-addr-by-name master-name命令可以得到該group的master,如果該Sentinel返回了null,那就重復上面的流程,重新連接下一個Sentinel。

    得到group的master后,連上master,發送ROLE命令,確認該master自身確實是作為master在運行。這個確認是必須的,如果Sentinel和group網絡分區了,那么該Sentinel認為的master就不會變化了,而group如果出現主從切換,此時Sentinel就拿不到真實的master了。如果ROLE得到的不再是master了,客戶端需要重復最前面的流程,重新連接下一個Sentinel。

    確認好master的ROLE也是master后,客戶端可以從每個Sentinel上訂閱消息。一般客戶端只要關心+switch-master即可,這個消息會告訴客戶端發生了主從切換,并把新老master的ip、port都推送在消息里。客戶端根據新的master,發送ROLE命令確認后,就可以和新的master通信了。

    有些客戶端希望把讀流量分給slave,那么可以通過SENTINEL slaves master-name命令來獲得該group下的slave列表。

    如果客戶端需要重連master,那么建議按照初始化連接的方式重新從Sentinel獲取master。

    如果采用連接池的方式,官方建議在每次有連接斷開需要重連的時候所有的連接都關閉,從而重建連接池。這么做也是為了防止新的連接獲取的master跟原來不一致了。

    客戶端還可以通過SENTINEL sentinels <master-name>命令更新自己的Sentinel列表,從而獲得最新存活的Sentinel。

    狀態持久化

    Sentinel的狀態會持久化到配置文件。例如每一次通過set命令設置新的配置,或者加入新節點的監控,修改的配置會持久化到配置文件,同時持久化配置的epoch,這意味著停止和重啟Sentinel是安全的。

    分區問題

    Sentinel集群會有分區問題,這個在官方文檔上有說明。

    在該圖中,Redis 3原來是master,網絡分區后,Sentinel1和Sentinel2會把Redis 1選舉為master。此時問題出現了,由于Sentinel3和Redis3處于另外分區,所以Sentinel3依然認為Redis3是master,此時處于分區內的Client B從Sentinel3獲得的master就是Redis3,而Redis3也認為自己是Redis3,客戶端依然能夠操作group,此時Client A和Client B操作的master已經不一樣了,同一個group出現了不一致現象。Redis官方給出了兩個建議。

    等分區恢復后,Client B在Redis3上的寫數據會丟失,如果你把Redis當做緩存,能夠接受這種現象,那么可以忽略這個問題。

    如果不想忽略這個問題,那么對于redis可以這樣配置

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

    min-slaves-to-write設置為1,保證了master認為至少有一個slave連接正常,master才能正常工作,上面Redis3因為已經與原來的兩個slave無法連接,所以Redis3此時已經無效了。(min-slaves-max-lag是主從ack延遲的最大時間。單位是秒)

    連接爆炸

    Sentinel還有個連接爆炸的問題。Sentinel的所有操作都是基于group進行的,不同group之間流程完全不干擾,Sentinel會去發現監控相同group的其他Sentinel,即使一個在其他group的Sentinel已經和本Sentinel建立了連接,在這個group內,也仍然會繼續建立連接,同時從這個連接發送ping命令確定其他Sentinel的存活,這個帶來的好處是流程簡潔,代碼清晰。但缺點就是帶來了連接數的消耗和大量的重復消息。下面我們從代碼的層面看下出現連接爆炸的原因。整個Sentinel主要就使用了兩個結構體。一個是sentinelState,用來記錄Sentinel的全局狀態。另一個是sentinelRedisInstance,用來記錄Sentinel監控的每一個節點的信息。sentinelState有一個記錄所有group的hash表,dict *masters。masters記錄了所有主節點的信息,hash表的鍵是主節點的名稱,值是一個sentinelRedisInstance結構。

    struct sentinelState {     ...     dict *masters;      /* Dictionary of master sentinelRedisInstances.                            Key is the instance name, value is the                            sentinelRedisInstance structure pointer. */     ... } sentinel; 

    sentinelRedisInstance結構里面記錄了整個group的詳細信息,其中包括slave的hash表和sentinel的hash表。

    typedef struct sentinelRedisInstance {     ...     dict *sentinels;    /* Other sentinels monitoring the same master. */     dict *slaves;       /* slaves for this master instance. */     ... } sentinelRedisInstance;     

    dict *sentinels記錄了監控該group除自己外的其他Sentinel。問題就出在這里。前面介紹了Sentinel發現其他Sentinel是通過訂閱hello頻道的消息。Sentinel會訂閱每個group的消息,然后當在一個group的hello頻道發現一個新的Sentinel后,Sentinel會為這個Sentinel生成一個新的sentinelRedisInstance結構,加入該group的sentinels的hash表里面。每次生成新的sentinelRedisInstance結構,都是從內存重新分配數據,重新和Sentinel建立連接。這樣如果另外也有一個Sentinel和自己監聽了相同group列表的話,他們會針對每個group彼此都建立一條連接。隨著Sentinel監聽的group針對,連接將成倍數的增加!這里還沒結束。Sentinel有個輪詢線程會監聽每個節點的狀態。這樣,對另一個Sentinel,本Sentinel會對每個group上建立的連接向另一個Sentinel發送Ping命令,從而產生了大量的重復消息。

    Sentinel命令列表

    以下對Sentinel的每個命令做個說明。

    * ping 用來探測節點的存活,正常會返回pong。 * sentinel 下面也很多子命令        masters(SENTINEL masterS) 獲得Sentinel監控的所有master信息        master  (SENTINEL master <name>) 獲得某個master信息        slaves  (SENTINEL slaveS <master-name>) 獲得某個master信息        sentinels  (SENTINEL SENTINELS <master-name>) 獲得監控同一個group的其他sentinel信息        is-master-down-by-addr  (SENTINEL IS-master-DOWN-BY-ADDR <ip> <port> <current-epoch> <runid>*/ 獲得該Sentinel對于某個master存活狀態,同時讓該Sentinel為自己投票        reset  (SENTINEL RESET <pattern>) 重置指定pattern的group信息        get-master-addr-by-name  (SENTINEL GET-master-ADDR-BY-NAME <master-name>)    獲得某個group下master信息        failover  (SENTINEL FAILOVER <master-name>) 主動觸發一次故障轉移        pending-scripts  (SENTINEL PENDING-SCRIPTS) 執行腳本        monitor  (SENTINEL MONITOR <name> <ip> <port> <quorum>) 開始監控某個group        remove  (SENTINEL REMOVE <name>) 移除對該group的監控        set       (SENTINEL SET <mastername> [<option> <value> ...]) sentinel動態設置Sentinel配置命令             down-after-milliseconds  (down-after-millisecodns <milliseconds>) 設置該<mastername> ping多長時間未響應才判定為離線,默認是30s             failover-timeout            (failover-timeout <milliseconds>) 故障轉移的超時時間,默認是180s             parallel-syncs            (parallel-syncs <milliseconds>) 同時讓多少個slave復制新的master,默認是1             notification-script       (notification-script <path>) 用于通知管理員的腳本的地址             client-reconfig-script       (client-reconfig-script <path>) 需要執行的腳本的地址             auth-pass                 (auth-pass <password>) group的master密碼             quorum                      (quorum <count>) 用于配置多少個Sentinel認為節點下線才認為客觀下線的數量。 * subscribe 訂閱某個頻道 * unsubscribe 退訂某個頻道 * psubscribe 訂閱某個模式,可以批量訂閱一些頻道 * punsubscribe 退訂某個模式 * publish 發布消息,用作測試。只能發布hello頻道的消息 * info 查看Sentinel信息。 * role    查看Sentinel和負責監控的master列表。 * shutdown 關閉Sentinel 

    推送的消息內容

    以下是Sentinel推送的所有消息。從Sentinel的推送消息,基本上可以看到Sentinel運行的整個流程。

    • +monitor quorum 有新的master被監控。
    • +reset-master master器已被重置。
    • +slave 一個新的slave已經被Sentinel檢測到并關聯到對應的master。
    • -pubsub-link 訂閱連接斷線。
    • -cmd-link 命令連接斷開。
    • +pubsub-link 推送訂閱連接連上的消息。
    • +cmd-link 推送命令連接連上的消息。
    • -cmd-link-reconnection 命令連接重連出錯。
    • -pubsub-link-reconnection 訂閱連接重連出錯。
    • +reboot 節點重啟,更換了新的runid。
    • +role-change new reported role is 通過解析info信息發現role可能有變化,role發生變化。
    • -role-change new reported role is 通過解析info信息發現role可能有變化,role沒有發生變化。
    • +promoted-slave 故障轉移過測中,新的master自己確認了master的角色。
    • +failover-state-reconf-slaves 該消息緊接著+promoted-slave消息,此時故障轉移已經進入了reconf-slaves狀態。
    • +convert-to-slave 新的master一段時間沒有確認自己的master角色,而且它原來的master已經運行正常了,則重新復制原來的master,自己重新做回slave。
    • +fix-slave-config slave現在的master地址和Sentinel保存的master不一致,則讓slave重新復制Sentinel認為的master。
    • +slave-reconf-inprog slave正在復制master。
    • +slave-reconf-done slave復制master完成。 * -dup-sentinel #duplicate of : or 通過hello頻道得到的Sentinel信息與已經保存的Sentinel信息產生沖突,需要被移除 —— 當 Sentinel 實例重啟的時候,就會出現這種情況。
    • +sentinel 一個新的Sentinel已經被Sentinel檢測到并關聯到對應的master。
    • +new-epoch 當前epoch被更新。
    • +config-update-from leader完成故障轉移后,其他Sentinel通過hello頻道獲得新的配置信息。
    • +switch-master <master-name> <old master ip> <old master port> <new master ip> <new master port> 配置變更,maseter的IP和port已經改變。這是絕大多數外部用戶都關心的信息。
    • -monitor 去掉了對該master的監控。
    • +set
    • +sdown 給定的實例現在處于主觀下線狀態。
    • -sdown 給定的實例已經不再處于主觀下線狀態。
    • +odown #quorum / 給定的實例現在處于客觀下線狀態。
    • -odown 給定的實例已經不再處于客觀下線狀態。
    • +vote-for-leader 把某個Sentinel設置為leader。
    • +try-failover 嘗試故障遷移操作,等待被大多數Sentinel選中。
    • -failover-abort-not-elected Sentinel 的當選時間已過,取消故障轉移計劃。
    • +elected-leader 贏得指定epoch的選舉,可以進行故障遷移操作。
    • +failover-state-select-slave 故障轉移步驟中開始選擇slave
    • -failover-abort-no-good-slave Sentinel沒有找到合適的slave提升為master,一段時間后將重試,但是也可能在重試的時候出現相同的找不到合適slave的情況。
    • +selected-slave 故障轉移中,選出了slave作為新的master。
    • +failover-state-send-slaveof-noone 故障轉移中,選出了slave作為新的master后準備把slave提升為master。
    • -failover-abort-slave-timeout slave提升為master超時。
    • +failover-state-wait-promotion 新master執行slaveof no one成功。
    • +failover-end-for-timeout 故障轉移超過時間,還有slave沒有復制完新的master。
    • +failover-end 故障轉移順利結束。
    • +slave-reconf-sent-be 故障轉移超過時間,還有slave沒有復制完新的master。這些slave將直接發送復制新master命令后就完成了整個故障轉移操作。
    • -slave-reconf-sent-timeout 超時的slave也正常完成復制工作。
    • +slave-reconf-sent leader Sentinel 向slave發送了復制命令,為slave設置新的master。
    • -tilt #tilt mode exited 退出 tilt 模式。
    • +tilt #tilt mode entered 進入 tilt 模式。

    參考資料:

    Redis 2.8.19 source code

    http://redis.io/topics/sentinel

    http://redis.io/topics/sentinel-clients

    http://redisdoc.com/topic/sentinel.html

    http://www.wzxue.com/redis核心解讀-集群管理工具redis-sentinel

    《In Search of an Understandable Consensus Algorithm》 Diego Ongaro and John Ousterhout Stanford University

    《Redis設計與實現》黃健宏 機械工業出版社

    posted on 2016-12-14 18:33 jinfeng_wang 閱讀(1566) 評論(0)  編輯  收藏 所屬分類: 2016-REDIS
    主站蜘蛛池模板: 免费av一区二区三区| 久久免费观看视频| 国产h视频在线观看网站免费| 久久亚洲精品视频| 国产在线一区二区综合免费视频| 久久久久亚洲av无码专区蜜芽| 黄色片免费在线观看| 亚洲一区二区三区日本久久九| 免费看又黄又无码的网站| 亚洲福利一区二区精品秒拍| 免费h片在线观看网址最新| 国产亚洲sss在线播放| 免费无码又爽又刺激高潮| 羞羞漫画登录页面免费| 亚洲色图综合在线| 久久国产精品免费专区| 亚洲精品乱码久久久久久下载| 男男AV纯肉无码免费播放无码| 亚洲最大av资源站无码av网址| 国产成人免费A在线视频| 一级成人a做片免费| 亚洲av色福利天堂| 好爽…又高潮了毛片免费看| 美女被爆羞羞网站免费| 亚洲国产精品va在线播放| 99在线视频免费观看视频| 国产AV日韩A∨亚洲AV电影| 亚洲av伊人久久综合密臀性色| 色播精品免费小视频| 菠萝菠萝蜜在线免费视频| 久久亚洲成a人片| 免费的一级片网站| 日本免费A级毛一片| 亚洲永久网址在线观看| 久久亚洲精品无码观看不卡| 在线永久看片免费的视频| a级毛片免费观看在线| 亚洲国产精品美女| 国产日产亚洲系列| 国产人在线成免费视频| 国产国产人免费人成成免视频|