來源:阿里中間件團隊分享 更多文章請關注 MAYOU18html
聊起微服務的服務註冊與發現,不少人立馬就會脫口而出 zk、etcd、consul、eureka 這些組件,進而聊到 CAP 如何取捨,性能如何,高可用和容災是怎麼實現的。
緩存
聊起微服務的服務註冊與發現,不少人立馬就會脫口而出 zk、etcd、consul、eureka 這些組件,進而聊到 CAP 如何取捨,性能如何,高可用和容災是怎麼實現的。安全
在這以前,站在組件使用者的角度,我想先問這麼幾個問題:服務器
註冊的 IP 和端口怎麼肯定 ?網絡
實現服務治理還須要註冊哪些信息 ?session
看完這些問題後,您也許會發現,對於服務註冊與發現,首先應該關注的是服務註冊發現自己的功能,而後纔是性能和高可用。負載均衡
一個好的服務註冊發現中間件,應該是能完整地知足服務開發和治理的基礎功能,而後纔是性能和高可用。若是沒有想清楚前面的功能,再高的可用性和性能都是浮雲。最後,安全也一樣重要。框架
服務端的性能如何 ?運維
服務發現的容災策略是怎樣的 ?socket
下面將從 服務註冊、服務發現、容災和高可用三個大方面來回答這些問題的主流作法。
最後會介紹一下 ANS(Alibaba Naming Service) , ANS 綜合了這些解決方案中的優勢,並在 EDAS(阿里巴巴企業級分佈式應用服務) 中輸出,目前徹底免費!
主流的 IP 獲取有這幾種方法。
最簡單粗暴的方式,手動配置須要註冊的IP。固然這種方式基本沒法在生產環境使用,由於微服務基本都是支持水平擴容多機部署的,在配置中寫死 IP 地址的方式沒法支持一份代碼水平擴容,會給運維帶來極大的成本。
經過遍歷網卡的方式去獲取,找到第一個不爲本地環回地址的 IP 地址。絕大多數狀況下,這個方式比較好用,dubbo 等框架採用的就是這種方法。
socket.getLocalAddress()
這種方式來獲取本機的 IP。端口的獲取,沒有標準化的方案。
若是是 RPC 應用,啓動的時候都有一個配置來指定服務監聽的端口, 註冊的時候直接使用配置項的端口值。
傳統的 WEB 容器所提供的 HTTP 的應用,一樣也存在一個配置文件來配置容器的監聽端口,註冊時候直接使用配置項的端口值。
EmbeddedServletContainerInitializedEvent. getEmbeddedServletContainer().getPort()
來獲取。(Spring Boot 版本爲 1.x)。簡單地將 IP 和 port 信息註冊上去,能夠知足基本的服務調用的需求,可是在業務發展到必定程度的時候,咱們還會有這些需求:
想知道某個 HTTP 服務是否開啓了 TLS。
對相同服務下的不一樣節點設置不一樣的權重,進行流量調度。
這些高級功能的實現,本質上是依賴於客戶端調用時候的負載均衡策略和調用策略,可是若是服務元數據沒有註冊上來,也只能是巧婦難爲無米之炊。一個良好的服務註冊中心在設計最初就應該支持這些擴展字段。
雖然服務註冊通常發生在服務的啓動階段,可是細分的話,服務註冊應該在服務已經徹底啓動成功,並準備對外提供服務以後才能進行註冊。
有些 RPC 框架自身提供了方法來判斷服務是否已經啓動完成,如 Thrift ,咱們能夠經過 Server.isServing() 來判斷。
有一些 RPC 框架自己沒有提供服務是否啓動完成的方式,這時咱們能夠經過檢測端口是否已經處於監聽狀態來判斷。
絕大多數的服務註冊中心都提供了健康檢查功能,在應用中止後會自動摘除服務所對應的節點。可是咱們也不能徹底依賴此功能,應用應該在中止時主動調用服務註冊中心的服務下線接口。
在 Java 應用中,通用的服務下線接口調用通常使用 JVM Shutdown Hook 的方式來實現。
特別的,在 Java 應用中的 Spring 框架中,能夠經過 Spring Bean LifeCycle 來實現應用中止時主動調用服務下線接口。
健康檢查分爲客戶端心跳和服務端主動探測兩種方式。
客戶端心跳
客戶端每隔必定時間主動發送「心跳」的方式來向服務端代表本身的服務狀態正常,心跳能夠是 TCP 的形式,也能夠是 HTTP 的形式。
也能夠經過維持客戶端和服務端的一個 socket 長鏈接本身實現一個客戶端心跳的方式。
ZooKeeper 並無主動的發送心跳,而是依賴了組件自己提供的臨時節點的特性,經過 ZooKeeper 鏈接的 session 來維持臨時節點。
可是客戶端心跳中,長鏈接的維持和客戶端的主動心跳都只是代表鏈路上的正常,不必定是服務狀態正常。
服務端主動調用服務進行健康檢查是一個較爲準確的方式,返回結果成功代表服務狀態確實正常。
服務端主動探測也存在問題。服務註冊中心主動調用 RPC 服務的某個接口沒法作到通用性;在不少場景下服務註冊中心到服務發佈者的網絡是不通的,服務端沒法主動發起健康檢查。
因此如何取捨,仍是須要根據實際狀況來決定,根據不一樣的場景,選擇不一樣的策略。
很經典的 Push 和 Pull 問題。
Push 的經典實現有兩種,基於 socket 長鏈接的 notify,典型的實現如 zookeeper;另外一種爲 HTTP 鏈接所使用 Long Polling。
可是基於 socket 長鏈接的 notify 和基於 HTTP 協議的 Long Polling 都會存在notify消息丟失的問題。
因此經過 Pull 的方式定時輪詢也必不可少,時間間隔的選擇也很關鍵,頻率越高服務註冊中心所承受的壓力也越大。須要結合服務端的性能和業務的規模進行權衡。
還有一種方式,真實的 Push,客戶端開啓一個 UDP server,服務註冊中心經過 UDP 的方式進行數據推送,固然這個也受限於網絡的連通性。
一個好的產品,用戶使用體驗和運維體驗必須是優雅的,若是查看本機發布和訂閱的服務,只能經過查看日誌,甚至是 jmap 的方式來獲取,顯然體驗很是糟糕。
服務註冊中心應該提供了豐富的接口,支持根據應用名、IP、訂閱服務名、發佈服務名,來進行多層次的組合查詢。
當服務節點數愈來愈多時,服務註冊中心的性能會成爲瓶頸,這時候就須要經過水平擴容來提高服務註冊中心集羣的性能。
首先,本地內存緩存,當運行時與服務註冊中心的鏈接丟失或服務註冊中心徹底宕機,仍能正常地調用服務。
而後,本地緩存文件,當應用與服務註冊中心發生網絡分區或服務註冊中心徹底宕機後,應用進行了重啓操做,內存裏沒有數據,此時應用能夠經過讀取本地緩存文件的數據來獲取到最後一次訂閱到的內容。
服務端的無狀態性保證了服務的容災和高可用能夠作的很薄。
鏈路安全,對於使用 HTTP 鏈接的服務註冊中心,保護鏈路安全的最好方式是使用 HTTPS。而使用 TCP 鏈接的服務註冊中心來講,因爲應用層協議通常使用的是私有協議,不必定存在現成的 TLS 支持方案。
在業務安全方面,應該在每一次的發佈、訂閱、心跳,都帶上鑑權的信息就行驗籤和鑑權,確保業務信息的安全性。
ANS (Alibaba Naming Service) 是阿里巴巴中間件團隊將多年業務實踐沉澱打磨的開源產品。在服務註冊與發現方面,ANS 綜合了上述解決方案中的優勢,是最適合雲原生應用的服務註冊與發現組件。
ANS 服務已經在 EDAS(阿里巴巴企業級分佈式應用服務) 上線,目前已經提供 Spring Cloud Ans Starter 方便 Spring Cloud 用戶直接使用一個安全的可靠的商業版服務註冊與發現功能。ANS 能完美地支持 Eureka 的特性,並且目前徹底免費!更多信息參見 EDAS 幫助文檔。