閱讀306 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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

  上一篇:go  《Netty IN ACTION》中文版《Netty實戰》翻譯手記——不負好時光
  下一篇:go  RDS SQL Server - 專題分享 - 巧用執行計劃緩存之索引缺失