列舉一個經常使用的Redis客戶端的併發模型
Redis爲單進程單線程模式,採用隊列模式將併發訪問變爲串行訪問。前端
Redis自己沒有鎖的概念,Redis對於多個客戶端鏈接並不存在競爭,可是在Jedis客戶端對Redis進行併發訪問時會發生鏈接超時、數據轉換錯誤、阻塞、客戶端關閉鏈接等問題,這些問題均是因爲客戶端鏈接混亂形成。node
對此有2種解決方法:redis
1.客戶端角度,爲保證每一個客戶端間正常有序與Redis進行通訊,對鏈接進行池化,同時對客戶端讀寫Redis操做採用內部鎖synchronized算法
2.服務器角度,利用setnx實現鎖。如某客戶端要得到一個名字list的鎖,客戶端使用下面的命令進行獲取:後端
Setnx lock.list current time + lock timeout數組
如返回1,則該客戶端得到鎖,把lock. list的鍵值設置爲時間值表示該鍵已被鎖定,該客戶端最後能夠經過DEL lock.list來釋放該鎖。緩存
如返回0,代表該鎖已被其餘客戶端取得,等對方完成或等待鎖超時服務器
列舉一個經常使用的消息中間件,若是消息要保序如何實現
針對這個問題,經過某種算法,將須要保持前後順序的消息放到同一個消息隊列中(kafka中就是partition, rabbitMq中就是queue)。而後只用一個消費者去消費該隊列。網絡
你的設計如何考慮Hash衝突
1.開放定址法(線性探測再散列,二次探測再散列,僞隨機探測再散列)數據結構
2.再哈希法
3.鏈地址法(Java hashmap就是這麼作的)
4.創建一個公共溢出區
一致性hash
在分佈式集羣中,對機器的添加刪除,或者機器故障後自動脫離集羣這些操做是分佈式集羣管理最基本的功能。若是採用經常使用的hash(object)%N算法,那麼在有機器添加或者刪除後,不少原有的數據就沒法找到了,這樣嚴重的違反了單調性原則
一致性哈希算法也是使用取模的方法,只是,剛纔描述的取模法是對服務器的數量進行取模,而一致性哈希算法是對2^32取模,什麼意思呢?咱們慢慢聊。
首先,咱們把二的三十二次方想象成一個圓,就像鐘錶同樣,鐘錶的圓能夠理解成由60個點組成的圓,而此處咱們把這個圓想象成由2^32個點組成的圓,示意圖以下:
圓環的正上方的點表明0,0點右側的第一個點表明1,以此類推,二、三、四、五、6……直到232-1,也就是說0點左側的第一個點表明232-1
咱們把這個由2的32次方個點組成的圓環稱爲hash環。
那麼,一致性哈希算法與上圖中的圓環有什麼關係呢?咱們繼續聊,仍然以以前描述的場景爲例,假設咱們有3臺緩存服務器,服務器A、服務器B、服務器C,那麼,在生產環境中,這三臺服務器確定有本身的IP地址,咱們使用它們各自的IP地址進行哈希計算,使用哈希後的結果對2^32取模,可使用以下公式示意。
hash(服務器A的IP地址) % 2^32
經過上述公式算出的結果必定是一個0到232-1之間的一個整數,咱們就用算出的這個整數,表明服務器A,既然這個整數確定處於0到232-1之間,那麼,上圖中的hash環上一定有一個點與這個整數對應,而咱們剛纔已經說明,使用這個整數表明服務器A,那麼,服務器A就能夠映射到這個環上,用下圖示意
同理,服務器B與服務器C也能夠經過相同的方法映射到上圖中的hash環中
hash(服務器B的IP地址) % 2^32
hash(服務器C的IP地址) % 2^32
經過上述方法,能夠將服務器B與服務器C映射到上圖中的hash環上,示意圖以下
假設3臺服務器映射到hash環上之後如上圖所示(固然,這是理想的狀況,咱們慢慢聊)。
好了,到目前爲止,咱們已經把緩存服務器與hash環聯繫在了一塊兒,咱們經過上述方法,把緩存服務器映射到了hash環上,那麼使用一樣的方法,咱們也能夠將須要緩存的對象映射到hash環上。
假設,咱們須要使用緩存服務器緩存圖片,並且咱們仍然使用圖片的名稱做爲找到圖片的key,那麼咱們使用以下公式能夠將圖片映射到上圖中的hash環上。
hash(圖片名稱) % 2^32
映射後的示意圖以下,下圖中的橘黃色圓形表示圖片
好了,如今服務器與圖片都被映射到了hash環上,那麼上圖中的這個圖片到底應該被緩存到哪一臺服務器上呢?上圖中的圖片將會被緩存到服務器A上,爲何呢?由於從圖片的位置開始,沿順時針方向遇到的第一個服務器就是A服務器,因此,上圖中的圖片將會被緩存到服務器A上,以下圖所示。
沒錯,一致性哈希算法就是經過這種方法,判斷一個對象應該被緩存到哪臺服務器上的,將緩存服務器與被緩存對象都映射到hash環上之後,從被緩存對象的位置出發,沿順時針方向遇到的第一個服務器,就是當前對象將要緩存於的服務器,因爲被緩存對象與服務器hash後的值是固定的,因此,在服務器不變的狀況下,一張圖片一定會被緩存到固定的服務器上,那麼,當下次想要訪問這張圖片時,只要再次使用相同的算法進行計算,便可算出這個圖片被緩存在哪一個服務器上,直接去對應的服務器查找對應的圖片便可。
剛纔的示例只使用了一張圖片進行演示,假設有四張圖片須要緩存,示意圖以下
1號、2號圖片將會被緩存到服務器A上,3號圖片將會被緩存到服務器B上,4號圖片將會被緩存到服務器C上。
一致性哈希算法的優勢
通過上述描述,我想兄弟你應該已經明白了一致性哈希算法的原理了,可是話說回來,一致性哈希算法可以解決以前出現的問題嗎,咱們說過,若是簡單的對服務器數量進行取模,那麼當服務器數量發生變化時,會產生緩存的雪崩,從而頗有可能致使系統崩潰,那麼使用一致性哈希算法,可以避免這個問題嗎?咱們來模擬一遍,便可獲得答案。
假設,服務器B出現了故障,咱們如今須要將服務器B移除,那麼,咱們將上圖中的服務器B從hash環上移除便可,移除服務器B之後示意圖以下。
在服務器B未移除時,圖片3應該被緩存到服務器B中,但是當服務器B移除之後,按照以前描述的一致性哈希算法的規則,圖片3應該被緩存到服務器C中,由於從圖片3的位置出發,沿順時針方向遇到的第一個緩存服務器節點就是服務器C,也就是說,若是服務器B出現故障被移除時,圖片3的緩存位置會發生改變
可是,圖片4仍然會被緩存到服務器C中,圖片1與圖片2仍然會被緩存到服務器A中,這與服務器B移除以前並無任何區別,這就是一致性哈希算法的優勢,若是使用以前的hash算法,服務器數量發生改變時,全部服務器的全部緩存在同一時間失效了,而使用一致性哈希算法時,服務器的數量若是發生改變,並非全部緩存都會失效,而是隻有部分緩存會失效,前端的緩存仍然能分擔整個系統的壓力,而不至於全部壓力都在同一時間集中到後端服務器上。
這就是一致性哈希算法所體現出的優勢。
hash環的偏斜
在介紹一致性哈希的概念時,咱們理想化的將3臺服務器均勻的映射到了hash環上,以下圖所示
可是,理想很豐滿,現實很骨感,咱們想象的與實際狀況每每不同。
在實際的映射中,服務器可能會被映射成以下模樣。
聰明如你必定想到了,若是服務器被映射成上圖中的模樣,那麼被緩存的對象頗有可能大部分集中緩存在某一臺服務器上,以下圖所示。
上圖中,1號、2號、3號、4號、6號圖片均被緩存在了服務器A上,只有5號圖片被緩存在了服務器B上,服務器C上甚至沒有緩存任何圖片,若是出現上圖中的狀況,A、B、C三臺服務器並無被合理的平均的充分利用,緩存分佈的極度不均勻,並且,若是此時服務器A出現故障,那麼失效緩存的數量也將達到最大值,在極端狀況下,仍然有可能引發系統的崩潰,上圖中的狀況則被稱之爲hash環的偏斜,那麼,咱們應該怎樣防止hash環的偏斜呢?一致性hash算法中使用"虛擬節點"解決了這個問題,咱們繼續聊。
虛擬節點
話接上文,因爲咱們只有3臺服務器,當咱們把服務器映射到hash環上的時候,頗有可能出現hash環偏斜的狀況,當hash環偏斜之後,緩存每每會極度不均衡的分佈在各服務器上,聰明如你必定已經想到了,若是想要均衡的將緩存分佈到3臺服務器上,最好能讓這3臺服務器儘可能多的、均勻的出如今hash環上,可是,真實的服務器資源只有3臺,咱們怎樣憑空的讓它們多起來呢,沒錯,就是憑空的讓服務器節點多起來,既然沒有多餘的真正的物理服務器節點,咱們就只能將現有的物理節點經過虛擬的方法複製出來,這些由實際節點虛擬複製而來的節點被稱爲"虛擬節點"。加入虛擬節點之後的hash環以下。
「虛擬節點"是"實際節點」(實際的物理服務器)在hash環上的複製品,一個實際節點能夠對應多個虛擬節點。
從上圖能夠看出,A、B、C三臺服務器分別虛擬出了一個虛擬節點,固然,若是你須要,也能夠虛擬出更多的虛擬節點。引入虛擬節點的概念後,緩存的分佈就均衡多了,上圖中,1號、3號圖片被緩存在服務器A中,5號、4號圖片被緩存在服務器B中,6號、2號圖片被緩存在服務器C中,若是你還不放心,能夠虛擬出更多的虛擬節點,以便減少hash環偏斜所帶來的影響,虛擬節點越多,hash環上的節點就越多,緩存被均勻分佈的機率就越大。
LRU算法
LRU是什麼?按照英文的直接原義就是Least Recently Used,最近最久未使用法,它是按照一個很是著名的計算機操做系統基礎理論得來的:最近使用的頁面數據會在將來一段時期內仍然被使用,已經好久沒有使用的頁面頗有可能在將來較長的一段時間內仍然不會被使用。基於這個思想,會存在一種緩存淘汰機制,每次從內存中找到最久未使用的數據而後置換出來,從而存入新的數據!它的主要衡量指標是使用的時間,附加指標是使用的次數。在計算機中大量使用了這個機制,它的合理性在於優先篩選熱點數據,所謂熱點數據,就是最近最多使用的數據!由於,利用LRU咱們能夠解決不少實際開發中的問題,而且很符合業務場景。
利用雙向鏈表實現
雙向鏈表有一個特色就是它的鏈表是雙路的,咱們定義好頭節點和尾節點,而後利用先進先出(FIFO),最近被放入的數據會最先被獲取。其中主要涉及到添加、訪問、修改、刪除操做。首先是添加,若是是新元素,直接放在鏈表頭上面(若是超出鏈表的size,移除最後一個元素);訪問的話,在頭節點的能夠不用管,若是是在中間位置或者尾巴,就要將數據移動到頭節點;修改操做也同樣,修改原值以後,再將數據移動到頭部;刪除的話,直接刪除;
slab分配,如何減小內存碎片
緩存失效的幾種形式
1 緩存穿透
緩存穿透是指查詢一個必定不存在的數據,因爲緩存是不命中時被動寫的,而且出於容錯考慮,若是從存儲層查不到數據則不寫入緩存,這將致使這個不存在的數據每次請求都要到存儲層去查詢,失去了緩存的意義。在流量大時,可能DB就掛掉了,要是有人利用不存在的key頻繁攻擊咱們的應用,這就是漏洞。 key不存在時,大量的數據進來查詢DB
解決方案:有不少種方法能夠有效地解決緩存穿透問題,最多見的則是採用布隆過濾器,將全部可能存在的數據哈希到一個足夠大的bitmap中,一個必定不存在的數據會被 這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。另外也有一個更爲簡單粗暴的方法(咱們採用的就是這種),若是一個查詢返回的數據爲空(無論是數 據不存在,仍是系統故障),咱們仍然把這個空結果進行緩存,但它的過時時間會很短,最長不超過五分鐘。
2 緩存雪崩(緩存失效)
緩存雪崩是指在咱們設置緩存時採用了相同的過時時間,致使緩存在某一時刻同時失效,請求所有轉發到DB,DB瞬時壓力太重雪崩。 key是多個
解決方案:緩存失效時的雪崩效應對底層系統的衝擊很是可怕。大多數系統設計者考慮用加鎖或者隊列的方式保證緩存的單線 程(進程)寫,從而避免失效時大量的併發請求落到底層存儲系統上。這裏分享一個簡單方案就時講緩存失效時間分散開,好比咱們能夠在原有的失效時間基礎上增長一個隨機值,好比1-5分鐘隨機,這樣每個緩存的過時時間的重複率就會下降,就很難引起集體失效的事件。 關鍵字: 時間岔開,確保你們的key不會落在同一個expire點上。
3 緩存擊穿(緩存併發)
對於一些設置了過時時間的key,若是這些key可能會在某些時間點被超高併發地訪問,是一種很是「熱點」的數據。這個時候,須要考慮一個問題:緩存被「擊穿」的問題,這個和緩存雪崩的區別在於這裏針對某一key緩存,前者則是不少key。 指同一個key。
解決方案:致使問題的緣由是同一時間查,同一時間寫緩存,致使併發下緩存也沒用,因此考慮使用單線程等方法將寫緩存保證只有一個去查了寫,其餘的使用緩存。
業界比較經常使用的作法,是使用mutex。簡單地來講,就是在緩存失效的時候(判斷拿出來的值爲空),不是當即去load db,而是先使用緩存工具的某些帶成功操做返回值的操做(好比Redis的SETNX或者Memcache的ADD)去set一個mutex key,當操做返回成功時,再進行load db的操做並回設緩存;不然,就重試整個get緩存的方法
什麼是布隆過濾器,其實現原理是? False positive指的是?
本質上布隆過濾器是一種數據結構,比較巧妙的機率型數據結構(probabilistic data structure),特色是高效地插入和查詢,能夠用來告訴你 「某樣東西必定不存在或者可能存在」。
相比於傳統的 List、Set、Map 等數據結構,它更高效、佔用空間更少,可是缺點是其返回的結果是機率性的,而不是確切的。
HashMap的問題
講述布隆過濾器的原理以前,咱們先思考一下,一般你判斷某個元素是否存在用的是什麼?應該蠻多人回答 HashMap 吧,確實能夠將值映射到 HashMap 的 Key,而後能夠在 O(1) 的時間複雜度內返回結果,效率奇高。可是 HashMap 的實現也有缺點,例如存儲容量佔比高,考慮到負載因子的存在,一般空間是不能被用滿的,而一旦你的值不少例如上億的時候,那 HashMap 佔據的內存大小就變得很可觀了。
還好比說你的數據集存儲在遠程服務器上,本地服務接受輸入,而數據集很是大不可能一次性讀進內存構建 HashMap 的時候,也會存在問題
布隆過濾器是一個 bit 向量或者說 bit 數組,長這樣:
若是咱們要映射一個值到布隆過濾器中,咱們須要使用多個不一樣的哈希函數生成多個哈希值,並對每一個生成的哈希值指向的 bit 位置 1,例如針對值 「baidu」 和三個不一樣的哈希函數分別生成了哈希值 一、四、7,則上圖轉變爲:
Ok,咱們如今再存一個值 「tencent」,若是哈希函數返回 三、四、8 的話,圖繼續變爲:
值得注意的是,4 這個 bit 位因爲兩個值的哈希函數都返回了這個 bit 位,所以它被覆蓋了。如今咱們若是想查詢 「dianping」 這個值是否存在,哈希函數返回了 一、五、8三個值,結果咱們發現 5 這個 bit 位上的值爲 0,說明沒有任何一個值映射到這個 bit 位上,所以咱們能夠很肯定地說 「dianping」 這個值不存在。而當咱們須要查詢 「baidu」 這個值是否存在的話,那麼哈希函數必然會返回 一、四、7,而後咱們檢查發現這三個 bit 位上的值均爲 1,那麼咱們能夠說 「baidu」 存在了麼?答案是不能夠,只能是 「baidu」 這個值可能存在。
這是爲何呢?答案跟簡單,由於隨着增長的值愈來愈多,被置爲 1 的 bit 位也會愈來愈多,這樣某個值 「taobao」 即便沒有被存儲過,可是萬一哈希函數返回的三個 bit 位都被其餘值置位了 1 ,那麼程序仍是會判斷 「taobao」 這個值存在
支持刪除麼
目前咱們知道布隆過濾器能夠支持 add 和 isExist 操做,那麼 delete 操做能夠麼,答案是不能夠,例如上圖中的 bit 位 4 被兩個值共同覆蓋的話,一旦你刪除其中一個值例如 「tencent」 而將其置位 0,那麼下次判斷另外一個值例如 「baidu」 是否存在的話,會直接返回 false,而實際上你並無刪除它。
如何解決這個問題,答案是計數刪除。可是計數刪除須要存儲一個數值,而不是原先的 bit 位,會增大佔用的內存大小。這樣的話,增長一個值就是將對應索引槽上存儲的值加一,刪除則是減一,判斷是否存在則是看值是否大於0
如何選擇哈希函數個數和布隆過濾器長度
很顯然,太小的布隆過濾器很快全部的 bit 位均爲 1,那麼查詢任何值都會返回「可能存在」,起不到過濾的目的了。布隆過濾器的長度會直接影響誤報率,布隆過濾器越長其誤報率越小。
另外,哈希函數的個數也須要權衡,個數越多則布隆過濾器 bit 位置位 1 的速度越快,且布隆過濾器的效率越低;可是若是太少的話,那咱們的誤報率會變高。
k 爲哈希函數個數,m 爲布隆過濾器長度,n 爲插入的元素個數,p 爲誤報率。
至於如何推導這個公式,我在知乎發佈的文章有涉及,感興趣能夠看看,不感興趣的話記住上面這個公式就好了。
最佳實踐
常見的適用常見有,利用布隆過濾器減小磁盤 IO 或者網絡請求,由於一旦一個值一定不存在的話,咱們能夠不用進行後續昂貴的查詢請求。
另外,既然你使用布隆過濾器來加速查找和判斷是否存在,那麼性能很低的哈希函數不是個好選擇,推薦 MurmurHash、Fnv 這些。
memcache與redis的區別
redis和memecache的不一樣在於[2]:
一、存儲方式:
memecache 把數據所有存在內存之中,斷電後會掛掉,數據不能超過內存大小
redis有部份存在硬盤上,這樣能保證數據的持久性,支持數據的持久化(筆者注:有快照和AOF日誌兩種持久化方式,在實際應用的時候,要特別注意配置文件快照參數,要不就頗有可能服務器頻繁滿載作dump)。
二、數據支持類型:
redis在數據支持上要比memecache多的多。
三、使用底層模型不一樣:
新版本的redis直接本身構建了VM 機制 ,由於通常的系統調用系統函數的話,會浪費必定的時間去移動和請求。
四、運行環境不一樣:
redis目前官方只支持LINUX 上去行,從而省去了對於其它系統的支持,這樣的話能夠更好的把精力用於本系統 環境上的優化,雖而後來微軟有一個小組爲其寫了補丁。可是沒有放到主幹上
我的總結一下,有持久化需求或者對數據結構和處理有高級要求的應用,選擇redis,其餘簡單的key/value存儲,選擇memcache。
zookeeper有什麼功能
簡單的說,zookeeper=文件系統+通知機制。
Zookeeper維護一個相似文件系統的數據結構:
每一個子目錄項如 NameService 都被稱做爲 znode,和文件系統同樣,咱們可以自由的增長、刪除znode,在一個znode下增長、刪除子znode,惟一的不一樣在於znode是能夠存儲數據的。
有四種類型的znode:
一、PERSISTENT-持久化目錄節點
客戶端與zookeeper斷開鏈接後,該節點依舊存在
二、 PERSISTENT_SEQUENTIAL-持久化順序編號目錄節點
客戶端與zookeeper斷開鏈接後,該節點依舊存在,只是Zookeeper給該節點名稱進行順序編號
三、EPHEMERAL-臨時目錄節點
客戶端與zookeeper斷開鏈接後,該節點被刪除
四、EPHEMERAL_SEQUENTIAL-臨時順序編號目錄節點
客戶端與zookeeper斷開鏈接後,該節點被刪除,只是Zookeeper給該節點名稱進行順序編號
監聽機制
客戶端註冊監聽它關心的目錄節點,當目錄節點發生變化(數據改變、被刪除、子目錄節點增長刪除)時,zookeeper會通知客戶端。
咱們能用zookeeper作什麼
這個彷佛最簡單,在zookeeper的文件系統裏建立一個目錄,即有惟一的path。在咱們使用tborg沒法肯定上游程序的部署機器時便可與下游程序約定好path,經過path即能互相探索發現,不見不散了。
程序老是須要配置的,若是程序分散部署在多臺機器上,要逐個改變配置就變得困難。好吧,如今把這些配置所有放到zookeeper上去,保存在 Zookeeper 的某個目錄節點中,而後全部相關應用程序對這個目錄節點進行監聽,一旦配置信息發生變化,每一個應用程序就會收到 Zookeeper 的通知,而後從 Zookeeper 獲取新的配置信息應用到系統中就好。
三、 集羣管理
所謂集羣管理無在意兩點:是否有機器退出和加入、選舉master。
對於第一點,全部機器約定在父目錄GroupMembers下建立臨時目錄節點,而後監聽父目錄節點的子節點變化消息。一旦有機器掛掉,該機器與 zookeeper的鏈接斷開,其所建立的臨時目錄節點被刪除,全部其餘機器都收到通知:某個兄弟目錄被刪除,因而,全部人都知道:它上船了。新機器加入 也是相似,全部機器收到通知:新兄弟目錄加入,highcount又有了。
四、 分佈式鎖
有了zookeeper的一致性文件系統,鎖的問題變得容易。鎖服務能夠分爲兩類,一個是保持獨佔,另外一個是控制時序
五、隊列管理
兩種類型的隊列:
一、 同步隊列,當一個隊列的成員都聚齊時,這個隊列纔可用,不然一直等待全部成員到達。
二、隊列按照 FIFO 方式進行入隊和出隊操做。
第一類,在約定目錄下建立臨時目錄節點,監聽節點數目是不是咱們要求的數目。
第二類,和分佈式鎖服務中的控制時序場景基本原理一致,入列有編號,出列按編號。
zk選舉算法如何進行
Zookeeper的核心是原子廣播,這個機制保證了各個Server之間的同步。實現這個機制的協議叫作Zab協議。Zab協議有兩種模式,它們分 別是恢復模式(選主)和廣播模式(同步)。當服務啓動或者在領導者崩潰後,Zab就進入了恢復模式,當領導者被選舉出來,且大多數Server完成了和 leader的狀態同步之後,恢復模式就結束了。狀態同步保證了leader和Server具備相同的系統狀態。
爲了保證事務的順序一致性,zookeeper採用了遞增的事務id號(zxid)來標識事務。全部的提議(proposal)都在被提出的時候加上 了zxid。實現中zxid是一個64位的數字,它高32位是epoch用來標識leader關係是否改變,每次一個leader被選出來,它都會有一個 新的epoch,標識當前屬於那個leader的統治時期。低32位用於遞增計數。
每一個Server在工做過程當中有三種狀態:
- LOOKING:當前Server不知道leader是誰,正在搜尋
- LEADING:當前Server即爲選舉出來的leader
- FOLLOWING:leader已經選舉出來,當前Server與之同步
選主流程
當leader崩潰或者leader失去大多數的follower,這時候zk進入恢復模式,恢復模式須要從新選舉出一個新的leader,讓全部的 Server都恢復到一個正確的狀態。Zk的選舉算法有兩種:一種是基於basic paxos實現的,另一種是基於fast paxos算法實現的。系統默認的選舉算法爲fast paxos。先介紹basic paxos流程:
1 .選舉線程由當前Server發起選舉的線程擔任,其主要功能是對投票結果進行統計,並選出推薦的Server;
2 .選舉線程首先向全部Server發起一次詢問(包括本身);
3 .選舉線程收到回覆後,驗證是不是本身發起的詢問(驗證zxid是否一致),而後獲取對方的id(myid),並存儲到當前詢問對象列表中,最後獲取對方提議的leader相關信息( id,zxid),並將這些信息存儲到當次選舉的投票記錄表中;
4. 收到全部Server回覆之後,就計算出zxid最大的那個Server,並將這個Server相關信息設置成下一次要投票的Server;
5. 線程將當前zxid最大的Server設置爲當前Server要推薦的Leader,若是此時獲勝的Server得到n/2 + 1的Server票數, 設置當前推薦的leader爲獲勝的Server,將根據獲勝的Server相關信息設置本身的狀態,不然,繼續這個過程,直到leader被選舉出來。
經過流程分析咱們能夠得出:要使Leader得到多數Server的支持,則Server總數必須是奇數2n+1,且存活的Server的數目不得少於n+1.
每一個Server啓動後都會重複以上流程。在恢復模式下,若是是剛從崩潰狀態恢復的或者剛啓動的server還會從磁盤快照中恢復數據和會話信息,zk會記錄事務日誌並按期進行快照,方便在恢復時進行狀態恢復。選主的具體流程圖以下所示:
fast paxos流程是在選舉過程當中,某Server首先向全部Server提議本身要成爲leader,當其它Server收到提議之後,解決epoch和 zxid的衝突,並接受對方的提議,而後向對方發送接受提議完成的消息,重複這個流程,最後必定能選舉出Leader。其流程圖以下所示:
同步流程
選完leader之後,zk就進入狀態同步過程。
1. leader等待server鏈接;
2 .Follower鏈接leader,將最大的zxid發送給leader;
3 .Leader根據follower的zxid肯定同步點;
4 .完成同步後通知follower 已經成爲uptodate狀態;
5 .Follower收到uptodate消息後,又能夠從新接受client的請求進行服務了。
流程圖以下所示: