聊聊微服務的服務註冊與發現!

趙鈺瑩發表於2018-06-26

一個好的服務註冊發現中介軟體,應該是能完整地滿足服務開發和治理的基礎功能,然後才是效能和高可用。如果沒有想清楚前面的功能,再高的可用性和效能都是浮雲。最後,安全也同樣重要。下面將從 服務註冊、服務發現、容災和高可用三個大方面來回答這些問題的主流做法。

阿里雲:聊聊微服務的服務註冊與發現!

引言

聊起微服務的服務註冊與發現,很多人立馬就會脫口而出 zk、etcd、consul、eureka 這些元件,進而聊到 CAP 如何取捨,效能如何,高可用和容災是怎麼實現的。

在這之前,站在元件使用者的角度,我想先問這麼幾個問題:

·註冊的 IP 和埠怎麼確定 ?

·實現服務治理還需要註冊哪些資訊 ?

·如何進行優雅的服務註冊與服務下線 ?

·註冊服務的健康檢查是如何做的 ?

·當服務有節點退出或新的節點加入時,訂閱者能不能及時收到通知 ?

·我能方便地檢視某個應用釋出和訂閱了哪些服務,以及所訂閱的服務有哪些節點嗎 ?

看完這些問題後,您也許會發現,對於服務註冊與發現,首先應該關注的是服務註冊發現本身的功能,然後才是效能和高可用。

一個好的服務註冊發現中介軟體,應該是能完整地滿足服務開發和治理的基礎功能,然後才是效能和高可用。如果沒有想清楚前面的功能,再高的可用性和效能都是浮雲。最後,安全也同樣重要。

·服務端的效能如何 ?

·服務發現的容災策略是怎樣的 ?

·當我的應用和服務發現中心的網路連線出現問題時,會對我的呼叫產生什麼影響 ?

·服務註冊中心某臺機器當機或者全部當機時,會對我的呼叫產生什麼影響 ?

·服務註冊和發現的鏈路安全嗎,有沒有做好許可權控制 ?

下面將從 服務註冊、服務發現、容災和高可用三個大方面來回答這些問題的主流做法。

最後會介紹一下 ANS(Alibaba Naming Service) , ANS 綜合了這些解決方案中的優點,並在 EDAS(阿里巴巴企業級分散式應用服務) 中輸出,目前完全免費!

服務註冊

·註冊的 IP 和埠怎麼確定 ?

IP 如何確定

·主流的 IP 獲取有這幾種方法。

最簡單粗暴的方式,手動配置需要註冊的IP。當然這種方式基本無法在生產環境使用,因為微服務基本都是支援水平擴容多機部署的,在配置中寫死 IP 地址的方式無法支援一份程式碼水平擴容,會給運維帶來極大的成本。

透過遍歷網路卡的方式去獲取,找到第一個不為本地環回地址的 IP 地址。絕大多數情況下,這個方式比較好用,dubbo 等框架採用的就是這種方法。

在一些網路規劃比較好的標準化機房中,我們還可以透過手動指定網路卡名,即 interfaceName 的方式來指定使用哪一塊網路卡所對應的 IP 地址進行註冊。

當上述三種方式都不能有效解決問題的時候,有一個方法就是直接與服務註冊中心建立 socket 連線,然後透過socket.getLocalAddress() 這種方式來獲取本機的 IP。

埠如何確定

埠的獲取,沒有標準化的方案。

如果是 RPC 應用,啟動的時候都有一個配置來指定服務監聽的埠, 註冊的時候直接使用配置項的埠值。

傳統的 WEB 容器所提供的 HTTP 的應用,同樣也存在一個配置檔案來配置容器的監聽埠,註冊時候直接使用配置項的埠值。

特別的,在 Java 應用的 Spring Boot 框架中,可以透過 EmbeddedServletContainerInitializedEvent. getEmbeddedServletContainer().getPort()來獲取。(Spring Boot 版本為 1.x)。

實現服務治理還需要註冊哪些資訊 ?

簡單地將 IP 和 port 資訊註冊上去,可以滿足基本的服務呼叫的需求,但是在業務發展到一定程度的時候,我們還會有這些需求:

想知道某個 HTTP 服務是否開啟了 TLS。

對相同服務下的不同節點設定不同的權重,進行流量排程。

將服務分成預發環境和生產環境,方便進行AB Test功能。

不同機房的服務註冊時加上機房的標籤,以實現同機房優先的路由規則。

這些高階功能的實現,本質上是依賴於客戶端呼叫時候的負載均衡策略和呼叫策略,但是如果服務後設資料沒有註冊上來,也只能是巧婦難為無米之炊。一個良好的服務註冊中心在設計最初就應該支援這些擴充套件欄位。

如何進行優雅的服務註冊與服務下線 ?

優雅釋出

雖然服務註冊一般發生在服務的啟動階段,但是細分的話,服務註冊應該在服務已經完全啟動成功,並準備對外提供服務之後才能進行註冊。

有些 RPC 框架自身提供了方法來判斷服務是否已經啟動完成,如 Thrift ,我們可以透過 Server.isServing() 來判斷。

有一些 RPC 框架本身沒有提供服務是否啟動完成的方式,這時我們可以透過檢測埠是否已經處於監聽狀態來判斷。

而對於 HTTP 服務,服務是否啟動完畢也可以透過埠是否處於監聽狀態來判斷。

特別的,在 Java 應用的 Spring Boot 框架中,可以透過事件通知的形式來通知容器已經啟動完畢, EmbeddedServletContainerInitializedEvent 事件來通知容器已經啟動完成 (Spring Boot 版本為 1.x)。

優雅下線

絕大多數的服務註冊中心都提供了健康檢查功能,在應用停止後會自動摘除服務所對應的節點。但是我們也不能完全依賴此功能,應用應該在停止時主動呼叫服務註冊中心的服務下線介面。

在 Java 應用中,通用的服務下線介面呼叫一般使用 JVM Shutdown Hook 的方式來實現。

特別的,在 Java 應用中的 Spring 框架中,可以透過 Spring Bean LifeCycle 來實現應用停止時主動呼叫服務下線介面。

當然上述兩種方式還不夠優雅,因為不能確保不出現 kill -9 這種粗暴的停止方式,而且應用呼叫服務下線介面也是嘗試去呼叫,對於網路不通等異常場景並沒有做異常處理。因此,呼叫客戶端仍應該做好負載均衡與 failover 的處理。

更優雅的方式,先將即將停止的應用所對應的權重調成 0,此時上游將不再呼叫此應用。這時候的停止應用的操作對服務訂閱者完全沒有影響,當然這種場景需要訂閱者實現按權重的負載均衡和運維部署工具深度結合。

服務的健康檢查是如何做的 ?

健康檢查分為客戶端心跳和服務端主動探測兩種方式。

客戶端心跳

客戶端每隔一定時間主動傳送“心跳”的方式來向服務端表明自己的服務狀態正常,心跳可以是 TCP 的形式,也可以是 HTTP 的形式。

也可以透過維持客戶端和服務端的一個 socket 長連線自己實現一個客戶端心跳的方式。

ZooKeeper 並沒有主動的傳送心跳,而是依賴了元件本身提供的臨時節點的特性,透過 ZooKeeper 連線的 session 來維持臨時節點。

但是客戶端心跳中,長連線的維持和客戶端的主動心跳都只是表明鏈路上的正常,不一定是服務狀態正常。

服務端主動呼叫服務進行健康檢查是一個較為準確的方式,返回結果成功表明服務狀態確實正常。

服務端主動探測

服務端呼叫服務釋出者某個 HTTP 介面來完成健康檢查。

對於沒有提供 HTTP 服務的 RPC 應用,服務端呼叫服務釋出者的介面來完成健康檢查。

可以透過執行某個指令碼的形式來進行綜合檢查。

服務端主動探測也存在問題。服務註冊中心主動呼叫 RPC 服務的某個介面無法做到通用性;在很多場景下服務註冊中心到服務釋出者的網路是不通的,服務端無法主動發起健康檢查。

所以如何取捨,還是需要根據實際情況來決定,根據不同的場景,選擇不同的策略。

服務發現

怎麼找到服務發現服務端的地址?

在應用的配置檔案中指定服務註冊中心的地址,類似於 zookeeper 和 eureka。

指定一個地址伺服器的地址,然後透過這個地址伺服器來獲取服務註冊中心的地址,地址伺服器返回的結果會隨著服務註冊中心的擴縮容及時更新。

當服務有節點退出或新的節點加入時,訂閱者如何及時收到通知 ?

很經典的 Push 和 Pull 問題。

Push 的經典實現有兩種,基於 socket 長連線的 notify,典型的實現如 zookeeper;另一種為 HTTP 連線所使用 Long Polling。

但是基於 socket 長連線的 notify 和基於 HTTP 協議的 Long Polling 都會存在notify訊息丟失的問題。

所以透過 Pull 的方式定時輪詢也必不可少,時間間隔的選擇也很關鍵,頻率越高服務註冊中心所承受的壓力也越大。需要結合服務端的效能和業務的規模進行權衡。

還有一種方式,真實的 Push,客戶端開啟一個 UDP server,服務註冊中心透過 UDP 的方式進行資料推送,當然這個也受限於網路的連通性。

我能方便地檢視我釋出和訂閱了哪些服務,訂閱的服務有哪些節點嗎 ?

一個好的產品,使用者使用體驗和運維體驗必須是優雅的,如果檢視本機發布和訂閱的服務,只能透過檢視日誌,甚至是 jmap 的方式來獲取,顯然體驗非常糟糕。

服務註冊中心應該提供了豐富的介面,支援根據應用名、IP、訂閱服務名、釋出服務名,來進行多層次的組合查詢。

同時,客戶端的記憶體裡,同樣也應該保留服務釋出與訂閱的各種資訊,並提供方式供人方便地查詢。

比如在 Java 中的 Spring Boot 的應用,可以結合 actuator endpoint,透過 HTTP 的方式來提供本機服務查詢功能,查詢此應用釋出的服務,以及訂閱的服務及各服務的對應節點。

容災和高可用

效能如何

當服務節點數越來越多時,服務註冊中心的效能會成為瓶頸,這時候就需要透過水平擴容來提升服務註冊中心叢集的效能。

對於那些採用了類 Paxos 協議的強一致性的元件,如ZooKeeper,由於每次寫操作需要過半的節點確認。水平擴容不能提升整個叢集的寫效能,只能提升整個叢集的讀效能。

而對於採用最終一致性的元件來說,水平擴容可以同時提升整個叢集的寫效能和讀效能。

客戶端容災策略

首先,本地記憶體快取,當執行時與服務註冊中心的連線丟失或服務註冊中心完全當機,仍能正常地呼叫服務。

然後,本地快取檔案,當應用與服務註冊中心發生網路分割槽或服務註冊中心完全當機後,應用進行了重啟操作,記憶體裡沒有資料,此時應用可以透過讀取本地快取檔案的資料來獲取到最後一次訂閱到的內容。

最後,本地容災資料夾。正常的情況下,容災資料夾內是沒有內容的。當服務端完全當機且長時間不能恢復,同時服務提供者又發生了很大的變更時,可以透過在容災資料夾內新增檔案的方式來開啟本地容災。此時客戶端會忽略原有的本地快取檔案,只從本地容災檔案中讀取配置。

服務端容災與高可用

當有新節點加入叢集時,節點啟動後能自動新增到地址伺服器中,並透過地址伺服器找到其他節點,自動從其他節點同步資料,以達到資料的最終一致性。

當某個節點當機時,此服務註冊中心節點的資訊會自動地址伺服器中摘除,客戶端能及時感知到此節點已下線。

服務端的無狀態性保證了服務的容災和高可用可以做的很薄。

服務端安全是如何做的 ?

鏈路安全,對於使用 HTTP 連線的服務註冊中心,保護鏈路安全的最好方式是使用 HTTPS。而使用 TCP 連線的服務註冊中心來說,由於應用層協議一般使用的是私有協議,不一定存在現成的 TLS 支援方案。

在業務安全方面,應該在每一次的釋出、訂閱、心跳,都帶上鑑權的資訊就行驗籤和鑑權,確保業務資訊的安全性。

Alibaba Naming Service

ANS (Alibaba Naming Service) 是阿里巴巴中介軟體團隊將多年業務實踐沉澱打磨的開源產品。在服務註冊與發現方面,ANS 綜合了上述解決方案中的優點,是最適合雲原生應用的服務註冊與發現元件。 ANS 服務已經在 EDAS(阿里巴巴企業級分散式應用服務) 上線,目前已經提供 Spring Cloud Ans Starter 方便 Spring Cloud 使用者直接使用一個安全的可靠的商業版服務註冊與發現功能。ANS 能完美地支援 Eureka 的特性,而且目前完全免費!更多資訊參見 EDAS 幫助文件。

來源:  作者: 中介軟體小哥

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31077337/viewspace-2156689/,如需轉載,請註明出處,否則將追究法律責任。

相關文章