Ribbon2 核心設計分析
Ribbon 提供基於軟件的負載均衡方式與目標集群中的機器進行通信。這裏忽略狀態統計部分,健康檢查的邏輯部分。
下麵是個典型的使用方式開始,先指定一些目標服務器地址,
//sample-client.ribbon.listOfServers=www.taobao.com:80,www.baidu.com:80,www.sina.com:80
然後借助ribbon 封裝的 httpClient 來發送請求(訪問站點首頁),循環20次是希望打印出負載均衡的效果。
...
RestClient client = (RestClient) ClientFactory.getNamedClient("sample-client");
HttpRequest request = HttpRequest.newBuilder().setUri(new URI("/")).build();
for (int i = 0; i < 20; i++) {
HttpResponse response = client.executeWithLoadBalancer(request);
System.out.println("Status code for " + response.getRequestedURI() + " :" + response.getStatus());
}
下麵按 Ribbon 的主要模塊進行設計分析,並在最後部分分析上述代碼的效果。
Ribbon-core 模塊
這一模塊中,主要定義通用的調用抽象。ribbon 的 client 處理請求並返回響應。
public interface IClient<S extends ClientRequest, T extends IResponse> {
public T execute(S request, IClientConfig requestConfig) throws Exception;
}
ClientRequest, 是獨立於所以具體實現的通信協議的抽象。它主要包括:uri(遠程資源的定位符),loadBalancerKey(Object類型,這是決定請求調用到目標服務器的關鍵信息),isRetriable; 當然它的具體實現類,可能還有請求headers,body等信息。
IResponse, 是服務端響應的抽象,主要包括: body(payload),isSuccess(響應狀態的簡化),響應headers,請求url;
VipAddress 術語,是一個地址的邏輯名,該邏輯名代表一係列目標服務器。比如“apple.bar:80”。
RetryHandler,用來決定哪些異常(比如:ConnectException,SocketTimeoutException)發生時要做重試,哪些異常(SocketException,SocketTimeoutException)發生時表示應該熔斷掉(對應服務器)的調用,默認的是不重試。
Ribbon-loadbalancer 模塊
負載均衡,宏觀上效果是希望將請求的流量均衡(不是簡單意義上的平均)的負載到提供服務的服務器之上。而對於每次請求而言,是通過計算拿到一個目標服務器地址的。
- ILoadBalancer
ILoadBalancer, 負載均衡器的接口。它的核心方法是 public Server chooseServer(Object key);
每次請求發生時根據參數傳入的 loadBalancerKey對象,決定出一個目標的服務器地址。Server 表示一個服務器(包括:主機地址,端口號以及一些元數據標識信息)。 那麼服務均衡器應該需要有一批可供選擇的目標服務集合吧,所以它還有個重要方法 addServers(List<Server> newServers)
,一般在啟動階段就要完成初始化地調用。
- BaseLoadBalancer
BaseLoadBalancer(實現 ILoadBalancer),這裏聲明了一個基礎lb,應該關聯個 IRule(負載均衡的規則)默認是輪詢規則:RoundRobinRule)。 它還默認支持一個Ping檢查功能(可以幫忙我們定時檢查目標服務器是否可通信)的***定時任務***,開發者可以在構造實例時明確指定 Iping(判斷一個服務是否還是活的),IPingStrateg 默認是串行地檢查策略,但如果目標服務地址過多,或者IPing執行過慢就不太合適。
IRule 路由規則,Rule 和 LoadBalancer 是一對一的相互關聯關係,Rule 是具體的負責均衡策略。常見的規則包括: 輪詢,隨機,基於響應的延遲等; public Server choose(Object key)
核心方法的輸入輸出基本一致,區別是 Rule 拿取目標 server list 是通過借助對應依賴的 LoadBalancer 拿到的。
- DynamicServerListLoadBalancer
DynamicServerListLoadBalancer(繼承 BaseLoadBalancer),事實上從啟動後一直不變的 ServerList 場景一般不太多,尤其在微服務場景:服務掛了,擴容,縮容等都會需要對服務消費方的客戶端的服務列表做出實時調整(通常借助服務發現產品:eureka,consul,zk...),DynamicServerListLoadBalancer 顧名思義就是針對該類場景的。要做到運行時實時更新,既要保證更新的實時可見,也要保證更新操作本身的同步。
ServerListUpdater,是動態服務列表的更新器。現實場景中一般有個專門提供發布和查詢/訂閱服務列表服務的角色,這裏暫時簡稱為”遠程注冊表服務”。更新 ServerList 常見的有兩種實現模式:push 或者 pull。 push 機製就是客戶端保持對“遠程注冊表服務”觀察就行,服務器發生變化時會,客戶端就會及時得到通知。 還有一種 pull 機製,一般是頻繁的發請求進行詢問“遠程注冊表服務”是否有變化發生。這個一般建議基於常見的服務發現產品進行實現。
ServerListFilter,是DynamicServerListLoadBalancer的可選構造參數之一。作用是在發生 ServerList 更新時篩選過濾出符合條件的一個子集。 因為 ServerList 可能比較大,包含成百上千台機器地址,如果都嚐試去調用,那麼客戶端的連接數就會非常多,這也會造成必要的消耗。
Ribbon-httpclient 模塊
ribbon-loadbalancer 模塊中有個 ClientFactory 靜態工具類,可以生成和管理多個名字唯一的 IClient 具體實例。ClientFactory.getNamedClient("sample-client");
,這裏因為未特殊配置(指定具體的 IClient 的實現類),所以選擇的是默認的Client實現類 com.netflix.niws.client.http.RestClient。
- RestClient
RestClient,是 ribbon-httpclient 模塊中針對 IClient 的具體實現,通過使用 Jesery Client (實際依賴 apacheHttpClient4 發送 http),同樣 HttpRequest 實現 ClientRequest,而HttpResponse 實現了 ClientResponse。
client.executeWithLoadBalancer(request)
,是client的父類 AbstractLoadBalancerAwareClient 中定義的方法,最終是調用 LoadBalancerContext#getServerFromLoadBalancer(URI,loadBalancerKey)
方法,該方法主要LB邏輯如下:
Server svc = lb.chooseServer(loadBalancerKey);
if (svc == null){
throw new ClientException(ClientException.ErrorType.GENERAL,
"Load balancer does not have available server for client: "
+ clientName);
}
//... check host not null
logger.debug("{} using LB returned Server: {} for request {}", new Object[]{clientName, svc, original});
return svc;
最後更新:2017-06-05 11:32:22