通過故事讓你輕鬆搞懂:Zookeeper是幹啥滴?

張大胖所在的公司這幾年發展得相當不錯,業務激增,人員也迅速擴展,轉眼之間,張大胖已經成為公司的“資深”員工了,更重要的是,經過這些年的不懈努力,他終於坐上了架構師的寶座。

但是大胖很快發現,這架構師真不是好當的,技術選型、架構設計,尤其是大家搞不定的技術難點,最終都得自己扛起來。溝通、說服、妥協、甚至爭吵都是家常便飯,比自己之前單純做開發的時候難多了。

公司的IT系統早已經從單機轉向了分佈式,分佈式系統帶來了巨大的挑戰。這週一剛上班,張大胖的郵箱裡已經塞滿了緊急郵件。

1.小梁的郵件

小梁的郵件裡說了一個RPC調用的問題,本來公司的架構組開發了一個RPC框架讓各個組去使用,但是各開發小組紛紛抱怨:這個RPC框架不支持動態的服務註冊和發現。

通過故事讓你輕鬆搞懂:Zookeeper是幹啥滴?

張大胖一看這個圖就明白怎麼回事了,為了支持高併發,OrderService被部署了4份,每個客戶端都保存了一份服務提供者的列表,但是這個列表是靜態的(在配置文件中寫死的),如果服務的提供者發生了變化,例如有些機器down了,或者又新增了OrderService的實例,客戶端根本不知道,可能還在傻乎乎地嘗試那些已經壞掉的實例呢!

想要得到最新的服務提供者的URL列表,必須得手工更新配置文件才行,確實很不方便。

對於這樣的問題,大胖馬上就意識到,這就是客戶端和服務提供者的緊耦合啊。

想解除這個耦合,非得增加一箇中間層不可!

張大胖想到,應該有個註冊中心,首先給這些服務命名(例如orderService),其次那些OrderService 都可以在這裡註冊一下,客戶端就到這裡來查詢,只需要給出名稱orderService,註冊中心就可以給出一個可以使用的url, 再也不怕服務提供者的動態增減了。

通過故事讓你輕鬆搞懂:Zookeeper是幹啥滴?

不知道是不是下意識的行為,張大胖把這個註冊中心的數據結構設計成為了一個樹形結構

通過故事讓你輕鬆搞懂:Zookeeper是幹啥滴?

/orderService 表達了一個服務的概念, 下面的每個節點表示了一個服務的實例。例如/orderService/node2表示的order service 的第二個實例, 每個節點上可以記錄下該實例的url , 這樣就可以查詢了。

當然這個註冊中心必須得能和各個服務實例通信,如果某個服務實例不幸down掉了,那它在樹結構中對於的節點也必須刪除, 這樣客戶端就查詢不到了。

通過故事讓你輕鬆搞懂:Zookeeper是幹啥滴?

嗯,可以在註冊中心和各個服務實例直接建立Session, 讓各個服務實例定期地發送心跳,如果過了特定時間收不到心跳,就認為這個服務實例掛掉了,Session 過期, 把它從樹形結構中刪除。

張大胖把自己的想法回覆了小梁,接著看小王的郵件。

2.小王的Master選舉

小王郵件中說的是三個Batch Job的協調問題,這三個Batch Job 部署在三臺機器上,但是這三個Batch Job同一個時刻只能有一個運行,如果其中某個不幸down掉,剩下的兩個就需要做個選舉,選出來的那個Batch Job 需要“繼承遺志”,繼續工作。

通過故事讓你輕鬆搞懂:Zookeeper是幹啥滴?

其實這就是一個Master的選舉問題,張大胖一眼就看出了本質。

只是為了選舉出Master, 這三個Batch Job 需要互通有無,互相協調才行,這就麻煩了!

要不弄個數據庫表?利用數據庫表主鍵不能衝突的特性,讓這三個Batch Job 都向同一個表中插入同樣的數據,誰先成功誰就是Master !

可是如果搶到Master的那個Batch Job掛掉了,別人永遠就搶不到了!因為記錄已經存在了, 別的Batch Job 沒法插入數據了!

嗯,還得加上定期更新的機制,如果一段時間內沒有更新就認為Master死掉了,別的Batch Job可以繼續搶..... 不過這麼做好麻煩!

換個思路,讓他們也去一個註冊中心去大吼一聲:“我是master!”, 誰的聲音大誰是Master 。

其實不是吼一聲,三個Batch Job啟動以後,都去註冊中心爭搶著去創建一個樹的節點(例如/master ),誰創建成功誰就是Master (當然註冊中心必須保證只能創建成功一次,其他請求就失敗了),其他兩個Batch Job就對這個節點虎視眈眈地監控,如果這個節點被刪除,就開始新一輪爭搶,去創建那個/master節點。

通過故事讓你輕鬆搞懂:Zookeeper是幹啥滴?

什麼時候節點會被刪除呢?對,就是當前Master的機器down掉了 !很明顯,註冊中心也需要和各個機器通信,看看他們是否活著。

通過故事讓你輕鬆搞懂:Zookeeper是幹啥滴?

等等,這裡還有一個複雜的情況, 如果機器1並沒有死掉,只是和註冊中心長時間連接不上,註冊中心會發現Session超時,會把機器1創建的/master刪除。讓機器2和機器3去搶,如果機器3成為了master, 開始運行Batch Job, 但是機器1並不知道自己被解除了Master的職務, 還在努力的運行Batch Job,這就衝突了!

看來機器1必須得能感知到和註冊中心的連接斷開了,需要停止Batch Job才行,等到和註冊中心再次連接上以後,才知道自己已經不是master了,老老實實地等下一次機會吧。

無論哪種方案,實現起來都很麻煩,這該死的分佈式!

先把思路給小王回覆一下吧。接著看小蔡的郵件。

3.小蔡的分佈式鎖

小蔡的郵件裡說的問題更加麻煩,有多個不同的系統(當然是分佈在不同的機器上!),要對同一個資源操作。

通過故事讓你輕鬆搞懂:Zookeeper是幹啥滴?

這要是在一個機器上,使用某個語言內置的鎖就可以搞定,例如Java的synchronized , 但是現在是分佈式啊,程序都跑在不同機器的不同進程中, synchcronized一點用都沒有了!

這是個分佈式鎖的問題啊!

能不能考慮下Master選舉問題中的方式,讓大家去搶?誰能搶先在註冊中心創建一個/distribute_lock的節點就表示搶到這個鎖了,然後讀寫資源,讀寫完以後就把/distribute_lock節點刪除,大家再來搶。

可是這樣的話某個系統可能會多次搶到,不太公平。

如果讓這些系統在註冊中心的/distribute_lock下都創建子節點, 然後給每個系統一個編號,會是這個樣子:

通過故事讓你輕鬆搞懂:Zookeeper是幹啥滴?

然後各個系統去檢查自己的編號,誰的編號小就認為誰持有了鎖, 例如系統1。

系統1持有了鎖,就可以對共享資源進行操作了, 操作完成以後process_01這個節點刪除, 再創建一個新的節點(編號變成process_04了):

通過故事讓你輕鬆搞懂:Zookeeper是幹啥滴?

其他系統一看,編號為01的刪除了,再看看誰是最小的吧,是process_02,那就認為系統2持有了鎖,可以對共享資源操作了。操作完成以後也要把process_02節點刪除,創建新的節點。這時候process_03就是最小的了,可以持有鎖了。

通過故事讓你輕鬆搞懂:Zookeeper是幹啥滴?

這樣循環往復下去...... 分佈式鎖就可以實現了!

看看,我設計的這個集中式的樹形結構很不錯吧,能解決各種各樣的問題! 張大胖不由得意起來。

好,先把這個想法告訴小蔡,實現細節下午開個會討論。

4.Zookeeper

正準備回覆小蔡的時候,大胖突然意識到,自己漏了一個重要的點,那就是

註冊中心的高可用性,如果註冊中心只有那麼一臺機器,一旦掛掉,整個系統就玩完了。

這個註冊中心也得有多臺機器來保證高可用性,那個自己頗為得意的樹形結構也需要在多個機器之間同步啊,要是有機器掛掉怎麼辦?通信超時怎麼辦?樹形結構的數據怎麼在各個機器之間保證強一致性?

小王、小梁、小蔡的原始問題沒有解決,單單是這個註冊中心就要了命了。以自己公司的技術實力,搞出一套這樣的註冊中心簡直是Mission Impossible !

大胖趕緊上網搜索,看看有沒有類似的解決方案,讓大胖感到萬分幸運的是,果然有一個,叫做Zookeeper

Zookeeper 所使用的樹形結構和自己想象的非常類似,更重要的是,人家實現了樹形結構數據在多臺機器之間的可靠複製,達到了數據在多臺機器之間的一致性。並且這多臺機器中如果有部分掛掉了/或者由於網絡原因無法連接上了, 整個系統還可以工作。

大胖趕快去看Zookeeper的關鍵概念和API:

1.

Session :表示某個客戶系統(例如Batch Job)和ZooKeeper之間的連接會話, Batch Job連上ZooKeeper以後會週期性地發送心跳信息, 如果Zookeepr在特定時間內收不到心跳,就會認為這個Batch Job已經死掉了, Session 就會結束。

2. znode : 樹形結構中的每個節點叫做znode, 按類型可以分為永久的znode(除非主動刪除,否則一直存在),臨時的znode(Session結束就會刪除)和 順序znode(就是小蔡的分佈式鎖中的process_01,process_02.....)。

3. Watch :某個客戶系統(例如Batch Job)可以監控znode, znode節點的變化(刪除,修改數據等)都可以通知Batch Job, 這樣Batch Job可以採取相應的動作,例如爭搶著去創建節點。

嗯,這些概念和接口應該可以滿足我們的要求了.


分享到:


相關文章: