一、前言
微服務的注冊中心目前主流的有以下四種:
- Zookeeper
- Eureka
- Consul
- Kube.NETes
那么實際開發中到底如何選擇呢?這是一個值得深入研究的事情,別著急,今天陳某就帶大家深入了解一下這四類注冊中心以及如何選型的問題。
二、為什么需要注冊中心?
隨著單體應用拆分,首當面臨的第一份挑戰就是服務實例的數量較多,并且服務自身對外暴露的訪問地址也具有動態性。可能因為服務擴容、服務的失敗和更新等因素,導致服務實例的運行時狀態經常變化,如下圖:
商品詳情需要調用營銷、訂單、庫存三個服務,存在問題有:
- 營銷、訂單、庫存這三個服務的地址都可能動態的發生改變,單存只使用配置的形式需要頻繁的變更,如果是寫到配置文件里面還需要重啟系統,這對生產來說太不友好了;
- 服務是集群部署的形式調用方負載均衡如何去實現;
解決第一個問題辦法就是用我們用偉人說過一句話,沒有什么是加一個中間層解決不了的,這個中間層就是我們的注冊中心。
解決第二問題就是關于負載均衡的實現,這個需要結合我們中間層老大哥來實現。
三、如何實現一個注冊中心?
對于如何實現注冊中心這個問題,首先將服務之間是如何交互的模型抽象出來,我們結合實際的案例來說明這個問題,以商品服務為例:
- 當我們搜索商品的時候商品服務就是提供者;
- 當我們查詢商品詳情的時候即服務的提供者又是服務的消費者,消費是訂單、庫存等服務;由此我們需要引入的三個角色就是:中間層(注冊中心)、生產者、消費者,如下圖:
整體的執行流程如下:
- 在服務啟動時,服務提供者通過內部的注冊中心客戶端應用自動將自身服務注冊到注冊中心,包含主機地址、服務名稱等等信息;
- 在服務啟動或者發生變更的時候,服務消費者的注冊中心客戶端程序則可以從注冊中心中獲取那些已經注冊的服務實例信息或者移除已下線的服務;
上圖還多一個設計緩存本地路由,緩存本地路由是為了提高服務路由的效率和容錯性,服務消費者可以配備緩存機制以加速服務路由。更重要的是,當服務注冊中心不可用時,服務消費者可以利用本地緩存路由實現對現有服務的可靠調用。
在整個執行的過程中,其中有點有一點是比較難的,就是服務消費者如何及時知道服務的生產者如何及時變更的,這個問題也是經典的生產者消費者的問題,解決的方式有兩種:
- 發布-訂閱模式:服務消費者能夠實時監控服務更新狀態,通常采用監聽器以及回調機制,經典的案例就是Zookeeper;
- 主動拉取策略:服務的消費者定期調用注冊中心提供的服務獲取接口獲取最新的服務列表并更新本地緩存,經典案例就是Eureka;
對于如何選擇這兩種方式,其實還有一個數據一致性問題可以聊聊,比如選擇定時器肯定就拋棄了一致性,最求的是最終一致,這里就不深入展開了,另外你可能還會說服務的移除等等這些功能都沒介紹,在我看來那只是一個附加功能,注冊中心重點還是在于服務注冊和發現,其他都是錦上添花罷了。
四、如何解決負載均衡的問題?
負載均衡的實現有兩種方式:
- 服務端的負載均衡;
- 客戶端的負載均衡; 對于實現的方案來說本質上是差不多的,只是說承接的載體不一樣,一個是服務端,一個客戶端,如下圖:
服務端的負載均衡,給服務提供者更強的流量控制權,但是無法滿足不同的消費者希望使用不同負載均衡策略的需求。
客戶端的負載均衡則提供了這種靈活性,并對用戶擴展提供更加友好的支持。但是客戶端負載均衡策略如果配置不當,可能會導致服務提供者出現熱點,或者壓根就拿不到任何服務提供者。
服務端負載均衡典型的代表就是Nginx,客戶端負載均衡典型代表是Ribbon,每種方式都有經典的代表,我們都是可以深入學習的。
常見的負載均衡器的算法的實現,常見的算法有以下六種:
1、輪詢法
將請求按順序輪流地分配到后端服務器上,它均衡地對待后端的每一臺服務器,而不關心服務器實際的連接數和當前的系統負載。
2、隨機法
通過系統的隨機算法,根據后端服務器的列表大小值來隨機選取其中的一臺服務器進行訪問。由概率統計理論可以得知,隨著客戶端調用服務端的次數增多;其實際效果越來越接近于平均分配調用量到后端的每一臺服務器,也就是輪詢的結果。
3、哈希算法
哈希的思想是根據獲取客戶端的IP地址,通過哈希函數計算得到的一個數值,用該數值對服務器列表的大小進行取模運算,得到的結果便是客服端要訪問服務器的序號。采用源地址哈希法進行負載均衡,同一IP地址的客戶端,當后端服務器列表不變時,它每次都會映射到同一臺后端服務器進行訪問。
4、加權輪詢法
不同的后端服務器可能機器的配置和當前系統的負載并不相同,因此它們的抗壓能力也不相同。給配置高、負載低的機器配置更高的權重,讓其處理更多的請;而配置低、負載高的機器,給其分配較低的權重,降低其系統負載,加權輪詢能很好地處理這一問題,并將請求順序且按照權重分配到后端。
5.加權隨機法
與加權輪詢法一樣,加權隨機法也根據后端機器的配置,系統的負載分配不同的權重。不同的是,它是按照權重隨機請求后端服務器,而非順序。
6.最小連接數法
最小連接數算法比較靈活和智能,由于后端服務器的配置不盡相同,對于請求的處理有快有慢,它是根據后端服務器當前的連接情況,動態地選取其中當前 積壓連接數最少的一臺服務器來處理當前的請求,盡可能地提高后端服務的利用效率,將負責合理地分流到每一臺服務器。
五、注冊中心如何選型?
現在注冊中心的選擇也是五花八門,現階段比較流行有以下幾種:
在介紹這個之前大家有些需要了解的知識有CAP、Paxos、Raft算法這里我就不進行過多介紹了。開始介紹以上5種實現注冊中心的方式。
1、Zookeeper
**Zookeeper:**這個說起來有點意思的是官方并沒有說他是一個注冊中心,但是國內Dubbo場景下很多都是使用Zookeeper來完成了注冊中心的功能。
當然這有很多歷史原因,這里我們就不追溯了,我還是來聊聊作為注冊中心使用的情況下,Zookeeper有哪些表現吧。
Zookeeper基礎概念
1、三種角色
Leader 角色:一個Zookeeper集群同一時間只會有一個實際工作的Leader,它會發起并維護與各Follwer及Observer間的心跳。所有的寫操作必須要通過Leader完成再由Leader將寫操作廣播給其它服務器。
Follower角色:一個Zookeeper集群可能同時存在多個Follower,它會響應Leader的心跳。Follower可直接處理并返回客戶端的讀請求,同時會將寫請求轉發給Leader處理,并且負責在Leader處理寫請求時對請求進行投票。
Observer角色:與Follower類似,但是無投票權。
2、四種節點
PERSISTENT-持久節點:除非手動刪除,否則節點一直存在于Zookeeper上
EPHEMERAL-臨時節點:臨時節點的生命周期與客戶端會話綁定,一旦客戶端會話失效,那么這個客戶端創建的所有臨時節點都會被移除。
PERSISTENT_SEQUENTIAL-持久順序節點:基本特性同持久節點,只是增加了順序屬性,節點名后邊會追加一個由父節點維護的自增整型數字。
EPHEMERAL_SEQUENTIAL-臨時順序節點:基本特性同臨時節點,增加了順序屬性,節點名后邊會追加一個由父節點維護的自增整型數字。
3、一種機制
Zookeeper的Watch機制,是一個輕量級的設計。因為它采用了一種推拉結合的模式。一旦服務端感知主題變了,那么只會發送一個事件類型和節點信息給關注的客戶端,而不會包括具體的變更內容,所以事件本身是輕量級的,這就是推的部分。然后,收到變更通知的客戶端需要自己去拉變更的數據,這就是拉的部分。
Zookeeper如何實現注冊中心?
簡單來講,Zookeeper可以充當一個服務注冊表(Service Registry),讓多個服務提供者形成一個集群,讓服務消費者通過服務注冊表獲取具體的服務訪問地址(ip+端口)去訪問具體的服務提供者。如下圖所示:
每當一個服務提供者部署后都要將自己的服務注冊到zookeeper的某一路徑上: /{service}/{version}/{ip:port} 。
比如我們的HelloWorldService部署到兩臺機器,那么Zookeeper上就會創建兩條目錄:
- /HelloWorldService/1.0.0/100.19.20.01:16888
- HelloWorldService/1.0.0/100.19.20.02:16888。
這么描述有點不好理解,下圖更直觀,
在Zookeeper中,進行服務注冊,實際上就是在Zookeeper中創建了一個Znode節點,該節點存儲了該服務的IP、端口、調用方式(協議、序列化方式)等。
該節點承擔著最重要的職責,它由服務提供者(發布服務時)創建,以供服務消費者獲取節點中的信息,從而定位到服務提供者真正IP,發起調用。通過IP設置為臨時節點,那么該節點數據不會一直存儲在 ZooKeeper 服務器上。
當創建該臨時節點的客戶端會話因超時或發生異常而關閉時,該節點也相應在 ZooKeeper 服務器上被刪除,剔除或者上線的時候會觸發Zookeeper的Watch機制,會發送消息給消費者,因此就做到消費者信息的及時更新。
Zookeeper從設計上來說的話整體遵循的CP的原則,在任何時候對 Zookeeper 的訪問請求能得到一致的數據結果,同時系統對網絡分區具備容錯性,在使用 Zookeeper 獲取服務列表時,如果此時的 Zookeeper 集群中的 Leader 宕機了,該集群就要進行 Leader 的選舉,又或者 Zookeeper 集群中半數以上服務器節點不可用(例如有三個節點,如果節點一檢測到節點三掛了 ,節點二也檢測到節點三掛了,那這個節點才算是真的掛了),那么將無法處理該請求。
所以說,Zookeeper 不能保證服務可用性。
2、Eureka
Netflix我感覺應該是在醞釀更好的東西的,下面我們重點還是來介紹Ereka 1.x相關的設計。
Eureka由兩個組件組成:Eureka服務端和Eureka客戶端。Eureka服務器用作服務注冊服務器。Eureka客戶端是一個JAVA客戶端,用來簡化與服務器的交互、作為輪詢負載均衡器,并提供服務的故障切換支持。
Eureka的基本架構,由3個角色組成:
- Eureka Server
提供服務注冊和發現功能;
- Service Provider服務提供方,將自身服務注冊到Eureka,從而使服務消費方能夠找到;
- Service Consumer服務消費方,從Eureka獲取注冊服務列表,從而能夠消費服務
Eureka 在設計時就緊遵AP原則,Eureka Server 可以運行多個實例來構建集群,解決單點問題,實例之間通過彼此互相注冊來提高可用性,是一種去中心化的架構,無 master/slave 之分,每一個實例 都是對等的,每個節點都可被視為其他節點的副本。
在集群環境中如果某臺 Eureka Server 宕機,Eureka Client 的請求會自動切換到新的 Eureka Server 節點上,當宕機的服務器重新恢復后,Eureka 會再次將其納入到服務器集群管理之中。
當節點開始接受客戶端請求時,所有的操作都會在節點間進行復制操作,將請求復制到該 Eureka Server 當前所知的其它所有節點中。
當一個新的 Eureka Server 節點啟動后,會首先嘗試從鄰近節點獲取所有注冊列表信息,并完成初始化。Eureka Server 通過 getEurekaServiceUrls() 方法獲取所有的節點,并且會通過心跳契約的方式定期更新。
默認情況下,如果 Eureka Server 在一定時間內沒有接收到某個服務實例的心跳(默認周期為30秒),Eureka Server 將會注銷該實例(默認為90秒, eureka.instance.lease-expiration-duration-in-seconds 進行自定義配置)。
當 Eureka Server 節點在短時間內丟失過多的心跳時,那么這個節點就會進入自我保護模式,這個測試環境的時候需要注意一下。
Eureka的集群中,只要有一臺Eureka還在,就能保證注冊服務可用,只不過查到的信息可能不是最新的(不保證強一致性)。
除此之外,Eureka還有一種自我保護機制,如果在15分鐘內超過**85%**的節點都沒有正常的心跳,那么Eureka就認為客戶端與注冊中心出現了網絡故障,此時會出現以下幾種情況:
- Eureka不再從注冊表中移除因為長時間沒有收到心跳而過期的服務;
- Eureka仍然能夠接受新服務注冊和查詢請求,但是不會被同步到其它節點上(即保證當前節點依然可用)
- 當網絡穩定時,當前實例新注冊的信息會被同步到其它節點中。
3、Nacos
Nacos 無縫支持一些主流的開源生態,如下圖:
Nacos 致力于幫助您發現、配置和管理微服務。Nacos 提供了一組簡單易用的特性集,幫助您快速實現動態服務發現、服務配置、服務元數據及流量管理。
Nacos 幫助您更敏捷和容易地構建、交付和管理微服務平臺。Nacos 是構建以“服務”為中心的現代應用架構 (例如微服務范式、云原生范式) 的服務基礎設施。
Nacos除了服務的注冊發現之外,還支持動態配置服務。動態配置服務可以讓您以中心化、外部化和動態化的方式管理所有環境的應用配置和服務配置。動態配置消除了配置變更時重新部署應用和服務的需要,讓配置管理變得更加高效和敏捷。配置中心化管理讓實現無狀態服務變得更簡單,讓服務按需彈性擴展變得更容易。
Nacos特點
服務發現和服務健康監測
Nacos 支持基于 DNS 和基于 RPC 的服務發現。服務提供者使用 原生SDK、OpenAPI、或一個獨立的Agent TODO注冊 Service 后,服務消費者可以使用DNS TODO 或HTTP&API查找和發現服務。
Nacos 提供對服務的實時的健康檢查,阻止向不健康的主機或服務實例發送請求。Nacos 支持傳輸層 (PING 或 TCP)和應用層 (如 HTTP、MySQL、用戶自定義)的健康檢查。對于復雜的云環境和網絡拓撲環境中(如 VPC、邊緣網絡等)服務的健康檢查,Nacos 提供了 agent 上報模式和服務端主動檢測2種健康檢查模式。Nacos 還提供了統一的健康檢查儀表盤,幫助您根據健康狀態管理服務的可用性及流量。
動態配置服務
動態配置服務可以讓您以中心化、外部化和動態化的方式管理所有環境的應用配置和服務配置。
動態配置消除了配置變更時重新部署應用和服務的需要,讓配置管理變得更加高效和敏捷。
配置中心化管理讓實現無狀態服務變得更簡單,讓服務按需彈性擴展變得更容易。
Nacos 提供了一個簡潔易用的UI (控制臺樣例 Demo) 幫助您管理所有的服務和應用的配置。Nacos 還提供包括配置版本跟蹤、金絲雀發布、一鍵回滾配置以及客戶端配置更新狀態跟蹤在內的一系列開箱即用的配置管理特性,幫助您更安全地在生產環境中管理配置變更和降低配置變更帶來的風險。
動態 DNS 服務
動態 DNS 服務支持權重路由,讓您更容易地實現中間層負載均衡、更靈活的路由策略、流量控制以及數據中心內網的簡單DNS解析服務。動態DNS服務還能讓您更容易地實現以 DNS 協議為基礎的服務發現,以幫助您消除耦合到廠商私有服務發現 API 上的風險。
Nacos 提供了一些簡單的 DNS APIs TODO 幫助您管理服務的關聯域名和可用的 IP:PORT 列表.
服務及其元數據管理
Nacos 能讓您從微服務平臺建設的視角管理數據中心的所有服務及元數據,包括管理服務的描述、生命周期、服務的靜態依賴分析、服務的健康狀態、服務的流量管理、路由及安全策略、服務的 SLA 以及最首要的 metrics 統計數據。
Nacos支持插件管理
關于Nacos數據的存儲來說,支持臨時也支持持久化。
關于設計來說支持CP也支持AP,對他來說只是一個命令的切換,隨你玩,還支持各種注冊中心遷移到Nacos,反正一句話,只要你想要的他就有。
4、Consul
Consul是HashiCorp公司推出的開源工具,Consul由Go語言開發,部署起來非常容易,只需要極少的可執行程序和配置文件,具有綠色、輕量級的特點。Consul是分布式的、高可用的、 可橫向擴展的用于實現分布式系統的服務發現與配置。
Consul的特點
服務發現(Service Discovery)
Consul提供了通過DNS或者HTTP接口的方式來注冊服務和發現服務。一些外部的服務通過Consul很容易的找到它所依賴的服務。
健康檢查(Health Checking)
Consul的Client可以提供任意數量的健康檢查,既可以與給定的服務相關聯(“webserver是否返回200 OK”),也可以與本地節點相關聯(“內存利用率是否低于90%”)。操作員可以使用這些信息來監視集群的健康狀況,服務發現組件可以使用這些信息將流量從不健康的主機路由出去。
Key/Value存儲
應用程序可以根據自己的需要使用Consul提供的Key/Value存儲。Consul提供了簡單易用的HTTP接口,結合其他工具可以實現動態配置、功能標記、領袖選舉等等功能。
安全服務通信
Consul可以為服務生成和分發TLS證書,以建立相互的TLS連接。意圖可用于定義允許哪些服務通信。服務分割可以很容易地進行管理,其目的是可以實時更改的,而不是使用復雜的網絡拓撲和靜態防火墻規則。
多數據中心
Consul支持開箱即用的多數據中心. 這意味著用戶不需要擔心需要建立額外的抽象層讓業務擴展到多個區域。
Consul支持多數據中心,在上圖中有兩個DataCenter,他們通過Internet互聯,同時請注意為了提高通信效率,只有Server節點才加入跨數據中心的通信。
在單個數據中心中,Consul分為Client和Server兩種節點(所有的節點也被稱為Agent),Server節點保存數據,Client負責健康檢查及轉發數據請求到Server;Server節點有一個Leader和多個Follower,Leader節點會將數據同步到Follower,Server的數量推薦是3個或者5個,在Leader掛掉的時候會啟動選舉機制產生一個新的Leader。
集群內的Consul節點通過gossip協議(流言協議)維護成員關系,也就是說某個節點了解集群內現在還有哪些節點,這些節點是Client還是Server。單個數據中心的流言協議同時使用TCP和UDP通信,并且都使用8301端口。跨數據中心的流言協議也同時使用TCP和UDP通信,端口使用8302。
集群內數據的讀寫請求既可以直接發到Server,也可以通過Client使用RPC轉發到Server,請求最終會到達Leader節點,在允許數據延時的情況下,讀請求也可以在普通的Server節點完成,集群內數據的讀寫和復制都是通過TCP的8300端口完成。
Consul其實也可以在應用內進行注冊,后續采用Spring Cloud全家桶這套做負載
我們這里聊聊關于Consul的應用外的注冊:
上圖主要多出來兩個組件,分別是Registrator和Consul Template,接下來我們介紹下這兩個組件如何結合可以實現在應用發進行服務發現和注冊。
Registrator:一個開源的第三方服務管理器項目,它通過監聽服務部署的 Docker 實例是否存活,來負責服務提供者的注冊和銷毀。
Consul Template:定時從注冊中心服務端獲取最新的服務提供者節點列表并刷新 LB 配置(比如 Nginx 的 upstream),這樣服務消費者就通過訪問 Nginx 就可以獲取最新的服務提供者信息,達到動態調節負載均衡的目的。
整體架構圖可能是這樣:
我們用Registrator來監控每個Server的狀態。當有新的Server啟動的時候,Registrator會把它注冊到Consul這個注冊中心上。
由于Consul Template已經訂閱了該注冊中心上的服務消息,此時Consul注冊中心會將新的Server信息推送給Consul Template,Consul Template則會去修改nginx.conf的配置文件,然后讓Nginx重新載入配置以達到自動修改負載均衡的目的。
5、Kubernetes
Kubernetes是一個輕便的和可擴展的開源平臺,用于管理容器化應用和服務。通過Kubernetes能夠進行應用的自動化部署和擴縮容。
在Kubernetes中,會將組成應用的容器組合成一個邏輯單元以更易管理和發現。Kubernetes積累了作為google生產環境運行工作負載15年的經驗,并吸收了來自于社區的最佳想法和實踐。
Kubernetes經過這幾年的快速發展,形成了一個大的生態環境,Google在2014年將Kubernetes作為開源項目。Kubernetes的關鍵特性包括:
- 自動化裝箱:在不犧牲可用性的條件下,基于容器對資源的要求和約束自動部署容器。同時,為了提高利用率和節省更多資源,將關鍵和最佳工作量結合在一起。
- 自愈能力:當容器失敗時,會對容器進行重啟;當所部署的Node節點有問題時,會對容器進行重新部署和重新調度;當容器未通過監控檢查時,會關閉此容器;直到容器正常運行時,才會對外提供服務。
- 水平擴容:通過簡單的命令、用戶界面或基于CPU的使用情況,能夠對應用進行擴容和縮容。
- 服務發現和負載均衡:開發者不需要使用額外的服務發現機制,就能夠基于Kubernetes進行服務發現和負載均衡。
- 自動發布和回滾:Kubernetes能夠程序化的發布應用和相關的配置。如果發布有問題,Kubernetes將能夠回歸發生的變更。
- 保密和配置管理:在不需要重新構建鏡像的情況下,可以部署和更新保密和應用配置。
- 存儲編排:自動掛接存儲系統,這些存儲系統可以來自于本地、公共云提供商(例如:GCP和AWS)、網絡存儲(例如:NFS、iSCSI、Gluster、Ceph、Cinder和Floker等)。
Kubernetes屬于主從分布式架構,主要由Master Node和Worker Node組成,以及包括客戶端命令行工具Kubectl和其它附加項。
Master Node:作為控制節點,對集群進行調度管理,Master主要由三部分構成:
- Api Server相當于 K8S 的網關,所有的指令請求都必須經過 Api Server;
- Kubernetes調度器,使用調度算法,把請求資源調度到某個 Node 節點;
- Controller控制器,維護 K8S 資源對象(CRUD:添加、刪除、更新、修改);
- ETCD存儲資源對象(可以服務注冊、發現等等);
Worker Node:作為真正的工作節點,運行業務應用的容器;Worker Node主要包含五部分:
- Docker是運行容器的基礎環境,容器引擎;
- Kuberlet 執行在 Node 節點上的資源操作,Scheduler 把請求交給Api ,然后 Api Sever 再把信息指令數據存儲在 ETCD 里,于是 Kuberlet 會掃描 ETCD 并獲取指令請求,然后去執行;
- Kube-proxy是代理服務,起到負載均衡作用;
- Fluentd采集日志;
- Pod:Kubernetes 管理的基本單元(最小單元),Pod 內部是容器。Kubernetes 不直接管理容器,而是管理 Pod;
6、總結
1、高可用
這幾款開源產品都已經考慮如何搭建高可用集群,這個地方有些差別而已;
2、關于CP還是AP的選擇
對于服務發現來說,針對同一個服務,即使注冊中心的不同節點保存的服務提供者信息不盡相同,也并不會造成災難性的后果。
但是對于服務消費者來說,如果因為注冊中心的異常導致消費不能正常進行,對于系統來說是災難性,因此我覺得對于注冊中心選型應該關注可用性,而非一致性,所以我選擇AP。
3、技術體系
對于語言來說我們都是Java技術棧,從這點來說我們更傾向于Eureka、Nacos;
如果公司內部有專門的中間件或者運維團隊的可以Consul、Kubernetes,畢竟Kubernetes才是未來,我們追求的就是框架內解決這些問題,不要涉及到應用內的業務開發,我們其實后者是有的,只是可能不能達到能自主研發程度,這樣只能要求自己走的遠一些。
應用內的解決方案一般適用于服務提供者和服務消費者同屬于一個技術體系;應用外的解決方案一般適合服務提供者和服務消費者采用了不同技術體系的業務場景。
關于Eureka、Nacos如何選擇,這個選擇就比較容易做了,那個讓我做的事少,我就選擇那個,顯然Nacos幫我們做了更多的事。