美团面试:如何设计一个注册中心?
?
?今天,給大家分享如何設計一個注冊中心。
不管是出于面試,還是深入學習注冊中心,關于如何設計一個注冊中心都是一個很好的話題。
假設現在我們系統有兩個小系統:
訂單系統
商品系統
單個系統分別部署在不同服務器上,如果我們訂單系統需要調用商品系統的某個服務:
怎么調用?
方法1:商品系統開發的朋友告訴你對應的地址。
方法2:商品系統開發的朋友把對應API地址存放到某個地方。
方法3:直接通過Nginx,使用域名進行轉發到某個實例上。
這時候,訂單系統就可以通過上述方法調用商品系統的API了。
問題來了
實際線上環境中,很少是單體機構的,很多都是做了集群的,也就是說每個服務會有N個實例,少則幾個幾十個,多則幾百上千上萬。如果此時我們還用上面三種方法,當我們的商品系統某個服務下線(宕機了),或者新增實例,此時是非常的頭疼。
所以,注冊中心就來了。
注冊中心來了
我們能不能搞一個第三方的節點,這個節點就用來存放我們商品系統的服務信息,這樣一來,其他系統需要服務信息,直接去第三方節點上去獲取即可。此時,其他系統只要知道這個第三方的節點地址就可以了。這個第三方的節點,我們也稱之為注冊中心。
下面我們用服務提供方(商品系統)稱之為provider,服務調用方(訂單系統)我們稱之為consumer。
如何設計一個注冊中心
我們需要解決如下幾個問題:
服務如何注冊
consumer如何知道provider
服務注冊中心如何高可用
服務上下線,消費端如何動態感知
服務注冊
當我們把服務信息注冊上去后,就應該是:
服務列表保存通常有三種方式:本地內存、數據庫、第三方緩存系統
注冊上去后,consumer需要服務地址的時候,就可以用相應key去注冊中心獲取對應的服務列表。
同一個服務注冊中心,我們可以注冊多個服務,比如用戶服務、商品服務、訂單服務...
服務消費
consumer端通過key獲取指定的服務地址列表。
以上的還是蠻簡單的吧,簡單來說,我們就是引用了一個第三方的服務來存放我們的服務提供者列表。并且以key-value的形式存儲,key我們可以理解為服務名稱,value就是服務實例列表。
注冊中心高可用
高可用無非就是做集群,我們可以對注冊中心部署多個節點。在消費端consumer只需要知道一個服務注冊中心集群地址cluster-url即可。
動態感知服務上下線
consumer拿到服務列表后,會把服務列表保存起來,保存到本地緩存里。
consumer通過一定的負載均衡算法,選擇出一個地址,最后發起遠程的調用。
如果我們的服務節點掛掉一個了,怎么辦?
此時,服務注冊中心的服務列表還是之前的列表,如果consumer調用到過掉的節點上,那豈不是會出問題呀。
所以,我們的服務注冊中心需要知道哪個服務節點掛了,然后從對應服務列表里刪除。
有種辦法叫做心跳檢測heartBeat,即就是服務注冊中心,每隔一定時間去監測一下provider,如果監測到某個服務掛了,那就把對應服務地址從服務列表中刪除。
根據心跳檢測,來提出無效服務。
可是不對呀,此時consumer端本地列表里還有過掉的服務地址,怎么辦呢?
或者是,在增加一個新的服務節點
對于服務注冊中心來說,就是服務列表里增加一個服務地址。
但是在消費端存在同樣的問題,就是服務注冊中心的服務列表和consumer端的服務列表不一樣了。
如何讓consumer端也動態感知呢?
其實很簡單,此時,我們得思維換一下,因為consumer的服務列表是來自于服務注冊中心,我們就可以把consumer理解為消費端,服務注冊中心理解為服務端。此時,consumer端就可以去服務端(服務注冊中心)拉取provider服務列表。
通常有兩種方案:push和pull
push:服務注冊中心主動推送服務列表給consumer。
pull:consumer主動從注冊中心拉取服務列表。
不管是push還是pull,都會存在consumer和服務注冊中心的通信管道。如果他們之間斷開了,那就無法獲取服務列表了。
還有就是服務注冊中心知道consumer的地址,比如
我得知道你的微信好友,不然我怎么把我手里的資源發給你
我們的網絡通信,必然會存在監聽的動作。
如果服務注冊中心要push到consumer,此時他們之間需要建立一個會話,所以,在服務注冊中心會維護一個會話管理的模塊。還有一種方式就是consumer提供一個API,這個API給服務注冊中心進行回調。
本質是我們是使用HTTP協議還是使用Socket監聽
push有個不好點,那就是服務注冊中心需要維護大量的會話,而且還需要對每個會話維持一個心跳,一遍知曉這些會話狀態,得確保這些consumer能收到數據,
另外就是pull,pull其實就相對push就簡單多了。pull和我們前面說的心跳機制是類似的,consumer端啟動定時任務,每個多久拉取服務注冊中心的服務列表。pull也不需要去維護大量的會話,我只需要每隔多久調用接口拉取服務列表即可。但是這里還是會存在一個問題,因為是定時去拉取,所以會存在一定的數據延遲,比如consumer剛剛拉取服務列表,但就在拉取結束的后,某個服務provider掛了,consumer就要等下次拉取才知道對應服務provider掛了。
如果定時任務是每隔30秒拉去一次,那就是說,延遲最長時間是30秒。
還有一種方式long-pull,也叫長輪詢,是上面兩種方案的優化方案,consumer發起拉取請求時,先把這個請求hold住,當服務注冊中心有發生變化后,consumer端能立馬感知。
關于長輪詢:
與簡單輪詢相似,只是在服務端在沒有新的返回數據情況下不會立即響應,而會掛起,直到有數據或即將超時
優點:實現也不復雜,同時相對輪詢,節約帶寬
缺點:還是存在占用服務端資源的問題,雖然及時性比輪詢要高,但是會在沒有數據的時候在服務端掛起,所以會一直占用服務端資源,處理能力變少
應用:一些早期的對及時性有一些要求的應用:web IM 聊天
這樣,我們就搞定了所謂的服務上下線動態感知。
通過上面的服務注冊、服務消費、注冊中心高可用以及動態感知服務的上下線,這就是我們去實現一個服務注冊中心的通用模型。
小總結
關于如何設計一個注冊中心,無非重點關以下幾點:
服務是如何注冊
消費端如何獲取服務
如何保證注冊中心的高可用
動態感知服務的上下線
有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
?
總結
以上是生活随笔為你收集整理的美团面试:如何设计一个注册中心?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 今天面了个腾讯的大佬,让我见识到了基础的
- 下一篇: DDD 领域驱动设计落地实践:六步拆解