什麼是服務發現
即我需要調用一個服務的時候,需要知道它的IP位址。
為什麼要服務端發現?
如果隻有每個服務隻有一個,且不會故障轉移,那麼直接代碼或者配置檔案中寫IP位址即可。但是如果服務多了,或者需要自動擴縮、故障轉移、無縫更新,那麼則需要服務發現。

正常的服務發現有兩種模式:
- 這裡的調用者成為消費者服務或者上遊服務。
- 提供者成為生産者服務或者下遊服務。
用戶端發現 client-side discovery
- 服務A啟動,将位址寫到注冊中心。停止則從注冊中心删除。
- 服務A和注冊中心保持心跳,動态重新整理。
- 調用者(用戶端,或者叫上遊服務)使用一個負載均衡算法,選擇一個服務去連接配接。
用戶端發現實作的大概邏輯:
- 簡易版:
直接啟動的時候寫到Redis或者資料庫作為系統資料庫,調用的時候直接取系統資料庫的資料就好了。
topic----IP
的hash結構。
萬一某個服務崩了,這個系統資料庫裡面會有髒資料,是以當調用方取出來發現調用失敗的時候,就删掉一下。
但是這樣的問題就是每個用戶端都需要實作負載均衡算法。
- 高配版:
寫一個注冊中心,其實也很簡單,就是有服務注冊的時候寫入一下Redis,有服務下線就删除。然後提供一個接口告知該服務有哪些可以調用的IP清單。
這裡值得注意的就是請求IP的對外方法。(敲黑闆)
這個注冊中心是單點的,雖然它的邏輯很簡單,不容易挂,但是它畢竟連接配接了所有服務,如果維持長連接配接心跳,這個量是很大的,如果每次通路服務都要請求一下IP,這個pv是非常龐大的。
微服務大多都是點查,比如用userID查使用者資料就是使用者服務一個方法,雖然可以暴露一個批量查詢的方法,但是服務的請求量還是非常大的,可以說是将原來的函數調用轉成了服務調用,可以想象,一個函數調用需要轉化為一個注冊中心請求IP+一個服務請求,是不是太慢了點。
有一個比較簡單地解決辦法:就是上遊服務連接配接注冊中心,擷取所有服務的指定IP,但是不要立即斷開,等30秒,直接根據這個IP清單調用下遊服務。此時如果注冊中心有更新,則傳回最新的,直到30秒逾時斷開。
這樣30秒一個連接配接,而且不會一直頻繁發包請求,壓力會大大減少。當然,終極方案還是将這個中心做成高可用的,比如主備、多套負載等。
用戶端發現的優缺點:
優點:
- 簡單
- 直連,減少了網絡跳轉。(了解了服務端發現模式就懂了)
缺點:(注冊中心可以解決,但是也帶來了單點問題)
- 需要維護服務間的心跳聯系。(系統資料庫告知的是IP,後面的關系需要上下遊自己維護)
是優點也是缺點:
- 每個上遊服務可以靈活安排自己的負載均衡算法。
- 需要實作多套負載均衡的算法,服務不一定是一種語言寫的,而且如果負載均衡算法涉及到更新,則需要所有服務重新打包。
服務端發現
既然想統一負載均衡算法,那麼我加一個反向代理就好了,比如nginx。消費者服務不再關心IP,隻需要提供topic+接口名,代理方用一定的負載均衡算法得到最優IP,直接路由過去。
服務端發現的優缺點
- 統一的負載均衡算法,友善重構、更新、疊代。
- 消費端不用關心IP,不用維持心跳,簡單了很多。
缺點:
- 加了一層轉發,相對會慢一點點。
- 路由轉發者會成為性能瓶頸。如果想把路由轉發再做成負載均衡,那就是無限套娃了(變成了怎麼找負載路由轉發者IP的問題)。
實作細節
從上面可以看出:
- 如果是小型業務,選簡配版用戶端發現模式就好。
- 中型業務,可以考慮服務端發現,大機率你的瓶頸不在轉發這裡。
- 大型業務,自然是自己寫注冊中心了。下面我們來看看注冊中心的實作細節。
注冊中心的實作
- 首先是需要維護一個系統資料庫,通常是用的redis,hash存儲。
- 方案A:
- 提供一個擷取所有IP的接口,和擷取單個topic的IP的接口。
- 消費者端自己實作負載均衡算法,可以自己定制。
- 方案B:
- 直接實作負載均衡算法,讓消費者在接口中傳參選擇用哪一種,提供一個每個topic的IP的接口。
- 然後消費者端需要進行30秒的長連接配接,一旦有人下線,則廣播給正在連接配接的消費端。
- 為生産端提供一個上線一個下線的接口。
消費者端和生産者端
- 需要統一實作一個心跳維持的邏輯(健康檢查),一旦生成者下線,則再次向注冊中心拉取IP清單,甚至可以附帶通知注冊中心它挂了。
- 生産者在上線時,先預熱好了再通知注冊中心,此時不用急。
- 生産者在下線時,先通知注冊中心下線,不要給我流量了,然後消化掉正在連接配接的消費者流量,停止健康檢查,再下線。
- 如果生産者崩了,則靠健康檢查,消費者會主動斷。
參考
- https://docshome.gitbook.io/microservices/4-service-discovery
- http://www.dockone.io/article/9057