微服务之服务发现
服務發現
為何使用服務發現
我們假設您正在編寫某些代碼,這些代碼調用了有 REST API 或 Thrift API 的服務。為了發送一個請求,您的代碼需要知道服務實例的網絡位置( IP 地址與端口)。在運行于物理硬件上的傳統應用中,服務實例的網絡位置是相對靜態的。例如,您的代碼可以從偶爾更新的配置文件中讀取網絡位置。
然而,在現代基于云的微服務應用中,這是一個更難解決的問題,如圖 4-1 所示。
服務實例具有動態分配的網絡位置。此外,由于自動擴縮、故障與升級,整組服務實例會動態變更。因此,您的客戶端代碼需要使用更精確的服務發現機制。
有兩種主要的服務發現模式:客戶端發現(client-side discovery)與服務端發現(server-side discovery)。讓我們先來看看客戶端發現。
客戶端發現模式
當使用客戶端發現模式時,客戶端負責確定可用服務實例的網絡位置和請求負載均衡。客戶端查詢服務注冊中心(service registry),它是可用服務實例的數據庫。之后,客戶端利用負載均衡算法選擇一個可用的服務實例并發出請求。
圖 4-2 展示了該模式的結構
服務實例的網絡位置在服務注冊中心啟動時被注冊。當實例終止時,它將從服務注冊中心中移除。通常使用心跳機制周期性地刷新服務實例的注冊信息。
Netflix OSS 提供了一個很好的客戶端發現模式示例。Netflix Eureka 是一個服務注冊中心,它提供了一個用于管理服務實例注冊和查詢可用實例的 REST API。Netflix Ribbon 是一個 IPC 客戶端,可與 Eureka 一起使用,用于在可用服務實例之間使請求負載均衡。
客戶端發現模式存在各種優點與缺點。該模式相對比較簡單,除了服務注冊中心,沒有其他移動部件。此外,由于客戶端能發現可用的服務實例,因此可以實現智能的,特定于應用程序的負載均衡決策,比如使用一致性哈希。該模式的一個重要缺點是它將客戶端與服務注冊中心耦合在一起。您必須為服務客戶端使用的每種編程語言和框架實現客戶端服務發現邏輯。
現在我們已經了解了客戶端發現,接下來讓我們看看服務端發現。
服務端發現模式
服務發現的另一種方式是服務端發現模式。圖 4-3 展示了該模式的結構:
AWS Elastic Load Balancer(ELB)是一個服務端發現路由示例。ELB 通常用于負載均衡來自互聯網的外部流量。然而,您還可以使用 ELB 來負載均衡虛擬私有云(VPC)內部的流量。客戶端通過 ELB 使用其 DNS 名稱來發送請求(HTTP 或 TCP)。ELB 負載均衡一組已注冊的 Elastic Compute Cloud(EC2)實例或 EC2 Container Service(ECS)容器之間的流量。這里沒有單獨可見的服務注冊中心。相反,EC2 實例與 ECS 容器由 ELB 本身注冊。
HTTP 服務器和負載均衡器(如 NGINX)也可以作為服務端發現負載均衡器。例如,使用 Consul Template 動態重新配置 NGINX 反向代理。Consul Template 是一個工具,可以從存儲在 Consul 服務注冊中心中的配置數據中定期重新生成任意配置文件。每當文件被更改時,它都會運行任意的 shell 命令。在列舉的博文描述的示例中,Consul Template 會生成一個 nginx.conf 文件,該文件配置了反向代理,然后通過運行一個命令告知 NGINX 重新加載配置的命令。更復雜的實現可以使用其 HTTP API 或 DNS 動態重新配置 NGINX。
某些部署環境(如 Kubernetes 和 Marathon)在群集中的每個主機上運行著一個代理。這些代理扮演著服務端發現負載均衡器角色。為了向服務發出請求,客戶端通過代理使用主機的 IP 地址和服務的分配端口來路由請求。 之后,代理將請求透明地轉發到在集群中某處運行的可用服務實例。
服務端發現模式有幾個優點與缺點。該模式的一大的優點是把發現的細節從客戶端抽象出來。客戶端只需向負載均衡器發出請求。這消除了為服務客戶端使用的每種編程語言和框架都實現發現邏輯的必要性。另外,如上所述,一些部署環境免費提供此功能。然而,這種模式存在一些缺點。除非負載均衡器由部署環境提供,否則您需要引入這個高可用系統組件,并進行設置和管理。
服務注冊中心
服務注冊中心(service registry)是服務發現的一個關鍵部分。它是一個包含了服務實例網絡位置的數據庫。服務注冊中心必須是高可用和最新的。雖然客戶端可以緩存從服務注冊中心獲得的網絡位置,但該信息最終會過期,客戶端將無法發現服務實例。因此,服務注冊中心由使用了復制協議(replication protocol)來維護一致性的服務器集群組成。
如之前所述, Netflix Eureka 是一個很好的服務注冊中心范例。它提供了一個用于注冊和查詢服務實例的 REST API。服務實例使用 POST 請求注冊其網絡位置。它必須每隔 30 秒使用 PUT 請求來刷新其注冊信息。通過使用 HTTP DELETE 請求或實例注冊超時來移除注冊信息。正如您所料,客戶端可以使用 HTTP GET 請求來檢索已注冊的服務實例。
Netflix 通過在每個 Amazon EC2 可用性區域(Availability Zone)中運行一個或多個 Eureka 服務器來實現高可用。每個 Eureka 服務器都運行在具有一個 Elastic IP地址的 EC2 實例上。 DNS TEXT 記錄用于存儲 Eureka 集群配置,這是一個從可用性區域到 Eureka 服務器的網絡位置列表的映射。當 Eureka 服務器啟動時,它將會查詢 DNS 以檢索 Eureka 群集配置,查找其對等體,并為其分配一個未使用的 Elastic IP 地址。
經過 Eureka 客戶端 — 服務與服務客戶端 — 查詢 DNS 以發現 Eureka 服務器的網絡位置。客戶端優先使用相同可用性區域中的Eureka 服務器,如果沒有可用的,則使用另一個可用性區域的 Eureka 服務器。
以下列舉了其他服務冊中心注:
- etcd 一個用于共享配置和服務發現的高可用、分布式和一致的鍵值存儲。使用了 etcd 的兩個著名項目分別為 Kubernetes 和 Cloud Foundry。
- Consul 一個發現與配置服務工具。它提供了一個 API,可用于客戶端注冊與發現服務。Consul 可對服務進行健康檢查,以確定服務的可用性。
- Apache ZooKeeper 一個被廣泛應用于分布式應用程序的高性能協調服務。 Apache ZooKeeper 最初是一個 Hadoop 子項目,但現在已經成為一個獨立的頂級項目。
另外,如之前所述,部分系統,如 Kubernetes、 Marathon 和 AWS,沒有明確的服務注冊中心。相反,服務注冊中心只是基礎設施的一個內置部分。
現在我們已經了解服務注冊中心的概念,接下來讓我們看看服務實例是如何被注冊到服務注冊中心。
服務注冊方式
如之前所述,服務實例必須在服務注冊中心中注冊與注銷。有幾種不同的方式來處理注冊和注銷。一是服務實例自我注冊,即自注冊模式。另一個是使用其他系統組件來管理服務實例的注冊,即第三方注冊模式。我們先來了解自注冊模式。
自注冊模式
當使用自注冊模式時,服務實例負責在服務注冊中心注冊和注銷自己。此外,如果有必要,服務實例將通過發送心跳請求來防止其注冊信息過期。
圖 4-4 展示了該模式的結構。
該方式的一個很好的范例就是 Netflix OSS Eureka 客戶端。Eureka 客戶端負責處理服務實例注冊與注銷的所有方面。實現了包括服務發現在內的多種模式的 Spring Cloud 項目可以輕松地使用 Eureka 自動注冊服務實例。您只需在 Java Configuration 類上應用 @EnableEurekaClient 注解即可
自注冊模式有好有壞。一個好處是它相對簡單,不需要任何其他系統組件。然而,主要缺點是它將服務實例與服務注冊中心耦合。您必須為服務使用的每種編程語言和框架都實現注冊代碼。
將服務與服務注冊中心分離的替代方法是第三方注冊模式。
第三方注冊模式
當使用第三方注冊模式時,服務實例不再負責向服務注冊中心注冊自己。相反,該工作將由被稱為服務注冊器(service registrar)的另一系統組件負責。服務注冊器通過輪詢部署環境或訂閱事件來跟蹤運行實例集的變更情況。當它檢測到一個新的可用服務實例時,它會將該實例注冊到服務注冊中心。此外,服務注冊器可以注銷終止的服務實例。
圖 4-5 展示了該模式的結構:
開源的 Registrator 項目是一個很好的服務注冊器示例。它可以自動注冊和注銷作為 Docker 容器部署的服務實例。注冊器支持多個服務注冊中心,包括 etcd 和 Consul。
另一個服務注冊器例子是 NetflixOSS Prana。其主要用于非 JVM 語言編寫的服務,它是一個與服務實例并行運行的側中應用。 Prana 使用了 Netflix Eureka 來注冊和注銷服務實例。
服務注冊器在部分部署環境中是一個內置組件。Autoscaling Group 創建的 EC2 實例可以自動注冊到 ELB。 Kubernetes 服務將自動注冊并提供發現。
第三方注冊模式同樣有好有壞。一個主要的好處是服務與服務注冊中心之間解耦。您不需要為開發人員使用的每種編程語言和框架都實現服務注冊邏輯。相反,僅需要在專用服務中以集中的方式處理服務實例注冊。
該模式的一個缺點是,除非部署環境內置,否則您同樣需要引入這樣一個高可用的系統組件,并進行設置和管理。
總結
在微服務應用程序中,運行的服務實例集會動態變更。實例具有動態分配的網絡位置。因此,為了讓客戶端向服務發出請求,它必須使用服務發現機制。
服務發現的一個關鍵部分是服務注冊中心。服務注冊中心是一個可用服務實例的數據庫。服務注冊中心提供了管理 API 和查詢 API 的功能。服務實例通過使用管理 API 從服務注冊中心注冊或者注銷。系統組件使用查詢 API 來發現可用的服務實例。有兩種主要的服務發現模式:客戶端發現與服務端發現。在使用了客戶端服務發現的系統中,客戶端查詢服務注冊中心,選擇一個可用實例并發出請求。在使用了服務端發現的系統中,客戶端通過路由進行請求,路由將查詢服務注冊中心,并將請求轉發到可用實例。
服務實例在服務注冊中心中注冊與注銷有兩種主要方式。一個是服務實例向服務注中心自我注冊,即自注冊模式。另一個是使用其他系統組件代表服務完成注冊與注銷,即第三方注冊模式。
在某些部署環境中,您需要使用如 Netflix Eureka 或 Apache ZooKeeper 等服務注冊中心來設置您自己的服務發現基礎設施。在其他部署環境中,服務發現是內置的,例如,Kubernetes 和 Marathon, 可以處理服務實例的注冊與注銷。他們還在每一個扮演服務端發現路由角色的集群主機上運行一個代理。
一個 HTTP 反向代理和負載均衡器(如 NGINX)也可以用作服務端發現負載均衡器。服務注冊中心可以將路由信息推送給 NGINX,并調用一個正常的配置更新;例如,您可以使用 Consul Template。 NGINX Plus 支持額外的動態重新配置機制 — 它可以使用 DNS 從注冊中心中提取有關服務實例的信息,并為遠程重新配置提供一個 API。
總結
- 上一篇: 技术大牛收割机!Java之父James
- 下一篇: 事件驱动数据管理 微服务和分布式数据管理