原創: 老劉 碼農翻身 https://mp.weixin.qq.com/s/J8erMBhiogXoQcn91SJcbw
張大胖所在的公司這幾年發展得相當不錯,業務激增,人員也迅速擴展,轉眼之間,張大胖已經成爲公司的“資深”員工了,更重要的是,經過這些年的不懈努力,他終於坐上了架構師的寶座。
但是大胖很快發現,這架構師真不是好當的,技術選型、架構設計,尤其是大家搞不定的技術難點,最終都得自己扛起來。溝通、說服、妥協、甚至爭吵都是家常便飯,比自己之前單純做開發的時候難多了。
公司的IT系統早已經從單機轉向了分佈式,分佈式系統帶來了巨大的挑戰。這週一剛上班,張大胖的郵箱裏已經塞滿了緊急郵件。
**
1.小梁的郵件
**
小梁的郵件裏說了一個RPC調用的問題,本來公司的架構組開發了一個RPC框架讓各個組去使用,但是各開發小組紛紛抱怨:這個RPC框架不支持動態的服務註冊和發現。
張大胖一看這個圖就明白怎麼回事了,爲了支持高併發,OrderService被部署了4份,每個客戶端都保存了一份服務提供者的列表,但是這個列表是靜態的(在配置文件中寫死的),如果服務的提供者發生了變化,例如有些機器down了,或者又新增了OrderService的實例,客戶端根本不知道,可能還在傻乎乎地嘗試那些已經壞掉的實例呢!
想要得到最新的服務提供者的URL列表,必須得手工更新配置文件纔行,確實很不方便。
對於這樣的問題,大胖馬上就意識到,這就是客戶端和服務提供者的緊耦合啊。
想解除這個耦合,非得增加一箇中間層不可!
張大胖想到,應該有個註冊中心,首先給這些服務命名(例如orderService),其次那些OrderService 都可以在這裏註冊一下,客戶端就到這裏來查詢,只需要給出名稱orderService,註冊中心就可以給出一個可以使用的url, 再也不怕服務提供者的動態增減了。
不知道是不是下意識的行爲,張大胖把這個註冊中心的數據結構設計成爲了一個樹形結構:
/orderService 表達了一個服務的概念, 下面的每個節點表示了一個服務的實例。 例如/orderService/node2表示的order service 的第二個實例, 每個節點上可以記錄下該實例的url , 這樣就可以查詢了。
當然這個註冊中心必須得能和各個服務實例通信,如果某個服務實例不幸down掉了,那它在樹結構中對於的節點也必須刪除, 這樣客戶端就查詢不到了。
嗯,可以在註冊中心和各個服務實例直接建立Session, 讓各個服務實例定期地發送心跳,如果過了特定時間收不到心跳,就認爲這個服務實例掛掉了,Session 過期, 把它從樹形結構中刪除。
張大胖把自己的想法回覆了小梁,接着看小王的郵件。
2.小王的Master選舉
小王郵件中說的是三個Batch Job的協調問題,這三個Batch Job 部署在三臺機器上,但是這三個Batch Job同一個時刻只能有一個運行,如果其中某個不幸down掉,剩下的兩個就需要做個選舉,選出來的那個Batch Job 需要“繼承遺志”,繼續工作。
其實這就是一個Master的選舉問題,張大胖一眼就看出了本質。
只是爲了選舉出Master, 這三個Batch Job 需要互通有無,互相協調纔行,這就麻煩了!
要不弄個數據庫表? 利用數據庫表主鍵不能衝突的特性,讓這三個Batch Job 都向同一個表中插入同樣的數據,誰先成功誰就是Master !
可是如果搶到Master的那個Batch Job掛掉了,別人永遠就搶不到了! 因爲記錄已經存在了, 別的Batch Job 沒法插入數據了!
嗯,還得加上定期更新的機制,如果一段時間內沒有更新就認爲Master死掉了,別的Batch Job可以繼續搶… 不過這麼做好麻煩!
換個思路,讓他們也去一個註冊中心去大吼一聲:“我是master!”, 誰的聲音大誰是Master 。
其實不是吼一聲,三個Batch Job啓動以後,都去註冊中心爭搶着去創建一個樹的節點(例如/master ),誰創建成功誰就是Master (當然註冊中心必須保證只能創建成功一次,其他請求就失敗了),其他兩個Batch Job就對這個節點虎視眈眈地監控,如果這個節點被刪除,就開始新一輪爭搶,去創建那個/master節點。
什麼時候節點會被刪除呢? 對,就是當前Master的機器down掉了 ! 很明顯,註冊中心也需要和各個機器通信,看看他們是否活着。
等等,這裏還有一個複雜的情況, 如果機器1並沒有死掉,只是和註冊中心長時間連接不上,註冊中心會發現Session超時,會把機器1創建的/master刪除。 讓機器2和機器3去搶,如果機器3成爲了master, 開始運行Batch Job, 但是機器1並不知道自己被解除了Master的職務, 還在努力的運行Batch Job,這就衝突了!
看來機器1必須得能感知到和註冊中心的連接斷開了,需要停止Batch Job纔行,等到和註冊中心再次連接上以後,才知道自己已經不是master了,老老實實地等下一次機會吧。
無論哪種方案,實現起來都很麻煩,這該死的分佈式!
先把思路給小王回覆一下吧。接着看小蔡的郵件。
3.小蔡的分佈式鎖
小蔡的郵件裏說的問題更加麻煩,有多個不同的系統(當然是分佈在不同的機器上!),要對同一個資源操作。
這要是在一個機器上,使用某個語言內置的鎖就可以搞定,例如Java的synchronized , 但是現在是分佈式啊,程序都跑在不同機器的不同進程中, synchcronized一點用都沒有了!
這是個分佈式鎖的問題啊!
能不能考慮下Master選舉問題中的方式,讓大家去搶? 誰能搶先在註冊中心創建一個/distribute_lock的節點就表示搶到這個鎖了,然後讀寫資源,讀寫完以後就把/distribute_lock節點刪除,大家再來搶。
可是這樣的話某個系統可能會多次搶到,不太公平。
如果讓這些系統在註冊中心的/distribute_lock下都創建子節點, 然後給每個系統一個編號,會是這個樣子:
然後各個系統去檢查自己的編號,誰的編號小就認爲誰持有了鎖, 例如系統1。
系統1持有了鎖,就可以對共享資源進行操作了, 操作完成以後process_01這個節點刪除, 再創建一個新的節點(編號變成process_04了):
其他系統一看,編號爲01的刪除了,再看看誰是最小的吧,是process_02,那就認爲系統2持有了鎖,可以對共享資源操作了。 操作完成以後也要把process_02節點刪除,創建新的節點。這時候process_03就是最小的了,可以持有鎖了。
這樣循環往復下去… 分佈式鎖就可以實現了!
看看,我設計的這個集中式的樹形結構很不錯吧,能解決各種各樣的問題! 張大胖不由得意起來。
好,先把這個想法告訴小蔡,實現細節下午開個會討論。
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可以採取相應的動作,例如爭搶着去創建節點。
嗯,這些概念和接口應該可以滿足我們的要求了, 就是它了,下午召集大家開會開始學習Zookeeper吧。
後記:本文從使用者的角度描述了Zookeeper有什麼用處,至於它內部是如何工作,那是另外一個Big topic了,我們以後再講。