一、什么是服務發現?
問題:
我們現在有多少個服務?
服務越來越多時,服務 URL 配置管理變得非常亂
服務對外的地址變了,其他所有有使用到的服務都要改地址
增加服務,增加服務實例等,都要做運維工作
1.1服務端發現模式
客戶端通過服務端的負載或路由訪問目標服務。
服務端負責負載和轉發請求,自身或者通過第三方工具,自動發現新的服務實例,並定時檢測心跳維護注冊表。
優點:
服務發現功能對於客戶端而言是透明的
缺點:
增加了一次轉發,集中式的做法沒有去中心化
提問:
1.nignx/F5有服務地址列表,也有負載均衡功能,屬於服務端發現嗎?
主要缺了“發現”功能,服務實例動態增減並不能主動識別
2.服務實例自己往服務端發送注冊請求不可以嗎?
服務實例是服務提供者,也可能是服務消費者;如果往注冊中心發送注冊請求,那么也可以從注冊中心拉注冊表,這樣直接做客戶端發現不是更好?
1.2客戶端發現模式
客戶端自身就可以確定服務提供者的可用實例地址列表和使用負載均衡策略。
服務提供者向注冊中心發送注冊和心跳。
服務端只負責維護注冊表,並對外提供服務。
優點:
客戶端可以靈活、智能地制定負載均衡策略
去中心化
缺點:
客戶端與注冊中心耦合,需要實現與注冊中心交互的代碼
二、服務發現的關鍵角色提取
服務提供者:
向注冊中心發送請求,發送信息
服務消費者:
向注冊中心發送請求,獲取信息
注冊中心:
提供接口給服務發現客戶端調用
維護服務實例信息集合
集群之間信息同步
三、關鍵角色要干些什么事情?
3.1服務提供者
服務提供者:向注冊中心發送請求,發送信息
1.發送請求問題
發送請求的工具用什么,okhttp、resttemplate?
請求失敗怎么辦,要重試嗎?重試的策略是什么?
2.注冊中心地址問題
發送信息給所有注冊中心還是發送一個成功就行了?
注冊中心有多個的話,優先訪問哪個呢,訪問失敗的要記錄起來下次不訪問了嗎?
地址寫在配置里,如果有增加或調整,所有客戶端都要改?
3.發送注冊/心跳問題
注冊和心跳調同一個接口還是分開兩個接口比較好?
注冊后,除了要定時發送心跳,實例信息有改變的情況下,要發給注冊中心嗎?
3.1.1服務提供者-發送請求問題
作為一個中間件如何提高可擴展性?
eureka的答案是裝飾器模式
JerseyClient
執行rest請求的工具,eureka有RestTemplate的替換實現方案
統計信息
根據請求類型進行統計,請求時間總計、請求失敗次數總計等
重定向
返回的http code是302則表示要重定向(注冊中心遷移?)更換重定向的url再次發起請求,最多重定向10次
失敗重試機制
已經請求失敗的server url記錄下來下次不再請求;
如果失敗的url過多(比如全部都失敗的情況,可能是客戶端網絡有問題),超過閥值,則清空失敗的url記錄,把他們重新當成可用的url ,這里要獲取注冊中心地址列表,用於重試
會話
請求失敗的url記錄多久,永久記錄嗎?使用session機制如果20分鍾沒請求過,則重置上面的所有機制
session有效時長20分鍾左右(有隨機增減)
3.1.2服務提供者-注冊中心地址問題
1.發送信息給所有注冊中心還是一個?
不管是AP還是CP,服務提供者只需與一個注冊中心交互成功就行,服務提供者不關心注冊中心之間的數據同步
2.客戶端如何保證發送到任意一個注冊中心成功呢?
配置多個注冊中心地址,失敗了就換下一個再試
3.注冊中心地址變更問題
配置了多個注冊中心地址后,如果注冊地址有增刪改怎么解決
將注冊中心地址放到配置中心 或 DNS txt記錄,客戶端定時去刷新獲取
(就算是存在本地配置文件,eureka也會開啟定時任務定時去取)
3.1.3服務提供者-發送心跳/注冊問題
注冊后,除了要定時發送心跳,實例信息有改變的情況下,要發給注冊中心嗎?
eureka的答案是需要:
1.實例狀態變更
2.配置信息變更
LeaseExpirationDurationInSeconds 心跳有效期
LeaseRenewalIntervalInSeconds 心跳頻率
3.健康檢查處理器的增減(用於獲取實例狀態)
3.2服務消費者
服務消費者:向注冊中心發送請求,獲取注冊表信息
1.發送請求問題(同服務提供者)
2.注冊中心地址變更問題(同服務提供者)
3.全量還是增量獲取應用實例信息
如果消費者本地實例信息為空,則全量獲取,否則增量獲取,關鍵邏輯在注冊中心,這里較簡單
將上面結論進行匯總
3.3注冊中心
注冊中心:
提供接口給服務發現客戶端調用
維護服務實例信息集合
集群之間數據同步
3.3.1注冊中心-接口
如何做增量更新?
查詢是否需要緩存?
如何做增量更新?
eureka的答案是維護一個隊列 recentlyChangedQueue
細心的觀眾會發現,為什么接收到心跳,不會增加到recentlyChangedQueue?
隊列里存的是引用類型,和應用實例列表是同一個地址,接收到心跳進行實例更新,定時維護最近變動實例隊列時查到實例信息變動,然后繼續將其保留在隊列里,這樣增量查詢下次一樣能查到該實例的信息
為什么要自我保護?
如果失效的實例過多,有沒有可能是注冊中心出了問題?
上一分鍾接收到心跳總數<= 實例總數*2(默認30s一次心跳) * RenewalPercentThreshold()(默認0.85)進入自我保護則不會剔除任何實例
3.3.2注冊中心-集群同步
如何做集群同步?
eureka的答案還是維護隊列,這里注冊、心跳、下線都會入隊,批量進行同步,失敗的加到失敗重試隊列
接收到的所有請求,都要同步嗎?
要經過過濾, 每種類型只取最新一條去同步即可
注冊中心地址變更
同服務發現客戶端一樣,定時刷新注冊中心地址,然后更新集群節點
3.4架構圖
(右鍵新標簽打開可查看大圖)
四、結合Region和AZ(Available Zone)
AWS(Amazon Web Services)
區域(Region):
從設計而言,每個 Amazon EC2 區域都與其他 Amazon EC2 區域完全隔離。這可實現最大程度的容錯能力和穩定性。在區域之間傳輸數據需要收費。
可用區(Availability Zone ,AZ):
如果您的實例分布在多個可用區且其中的某個實例發生故障,則您可對您的應用程序進行相應設計,以使另一可用區中的實例可代為處理相關請求。可用區由區域代碼后跟一個字母標識符表示;例如,us-east-1a。
阿里雲
地域(Region):
不同地域的雲服務器 ECS、關系型數據庫 RDS、對象存儲服務 OSS 內網不互通。
不同地域之間的雲服務器 ECS 不能跨地域部署負載均衡,即在不同的地域購買的 ECS 實例不支持跨地域部署在同一負載均衡實例下。
可用區(Zone):
可用區是指在同一地域內,電力和網絡互相獨立的物理區域。同一可用區內實例之間的網絡延時更小。
經過對比,其實AWS和阿里雲兩者關於region和az的差別並不大
eureka關於Region和AZ(Available Zone)的架構圖
理解region和az的概念后,我們需要做什么是不是就簡單多了?
1.服務發現客戶端,優先與相同的AZ進行通訊
2.服務發現客戶端,查詢注冊中心地址列表時,只取相同region下所有AZ的注冊中心地址
2.注冊中心,也只需要將數據同步給相同region的所有AZ即可
不過屬於哪個區,好像沒有特殊的配置吧?
eureka.client.availability-zones.us-east-1=zone2,zone1
eureka中取上面配置的第一個可用區為優先使用的可用區
注:雖然region之間是數據隔離的,但是eureka也支持從其他region同步信息(通過http請求)
常見問題探討
連連看,將如下類圖與我們架構圖里對象進行對應(右鍵新標簽打開可查看大圖)
eureka server 是ap還是cp?
服務啟動后會不會馬上注冊到注冊中心?
服務提供者注冊到注冊中心后,服務消費者最快多久能夠查詢到該提供者?
為什么接收心跳后不入隊,那么更新心跳后啟不是增量查詢查不到了?
集群同步的隊列,為什么要經過一道中轉才進行消費?
附錄: