Ribbon 是 Netflix 發佈的雲中間層服務開源項目,其主要功能是提供客戶側軟件負載均衡算法,將 Netflix 的中間層服務連接在一起。
Why Ribbon?
Ribbon 的區域感知負載均衡器的功能久經考驗。區域感知負載均衡器內置電路跳閘邏輯,可被配置基於區域同源關係(Zone Affinity,也就是更傾向於選擇發出調用的服務所在的託管區域內,這樣可用降低延遲,節省成本)選擇目標服務實例。它監控每個區域中運行的實例的運維行爲,而且能夠實時快速丟棄一整個區域。在面對整個區域的故障時,這幫我們提升了彈性。簡單的來說,在配置文件中列出LB後面所有的機器,ribbon 會自動幫你去連這些機器(基於某種規則,如隨機連接、輪流連接,基於響應時間等等)。我們很方便地使用 Ribbon 實現自定義負載均衡算法。
Ribbon 的其他一些特性
- 發現服務,比如需要增加一些機器,可以實現 Ribbon 的一個接口將這個機器加到列表裏(請參考《Ribbon 和 wowza 的集成開發》的動態修改負載節點案例);
- 對 Cloud 平臺的支持:比如對 AWS 來說,有參數支持只返回同一個 zone 的服務器地址,這樣避免跨 zone 調用所引起的網絡延時;
- 故障恢復能力:可以將一些不可用的服務器剔除。
Ribbon 時序圖
如何使用 Ribbon?
Ribbon 提供了框架來支持通用的 LB 機制,同時也提供了對 Netflix eureka 框架的支持。接下來我們來看一下如何在 Java 項目裏使用 Ribbon。
配置文件(sample-client.properties)
- # Max number of retries on the same server (excluding the first try)
- sample-client.ribbon.MaxAutoRetries=1
- # Max number of next servers to retry (excluding the first server)
- sample-client.ribbon.MaxAutoRetriesNextServer=1
- # Whether all operations can be retried for this client
- sample-client.ribbon.OkToRetryOnAllOperations=true
- # Interval to refresh the server list from the source
- sample-client.ribbon.ServerListRefreshInterval=2000
- # Connect timeout used by Apache HttpClient
- sample-client.ribbon.ConnectTimeout=3000
- # Read timeout used by Apache HttpClient
- sample-client.ribbon.ReadTimeout=3000
- # Initial list of servers, can be changed via Archaius dynamic property at runtime
- sample-client.ribbon.listOfServers=www.microsoft.com:80,www.yahoo.com:80,www.google.com:80
每個條目的格式是爲
<clientName>.<nameSpace>.<propertyName>=<value>
clientName 將被接下來的工廠用於創建客戶端。nameSpace 是可配置的,默認是 "ribbon"。常用屬性名可以從 CommonClientConfigKey 獲取。
這些屬性也可以被定義作系統屬性進行獲取,也可以以 Archaius 加載的任何屬性資源進行獲取。
Java 代碼測試
- public static void main(String[] args) throws Exception {
- ConfigurationManager.loadPropertiesFromResources("sample-client.properties"); // 1
- System.out.println(ConfigurationManager.getConfigInstance().getProperty("sample-client.ribbon.listOfServers"));
- RestClient client = (RestClient) ClientFactory.getNamedClient("sample-client"); // 2
- HttpClientRequest request = HttpClientRequest.newBuilder().setUri(new URI("/")).build(); // 3
- for (int i = 0; i < 20; i++) {
- HttpClientResponse response = client.executeWithLoadBalancer(request); // 4
- System.out.println("Status code for " + response.getRequestedURI() + " :" + response.getStatus());
- }
- ZoneAwareLoadBalancer lb = (ZoneAwareLoadBalancer) client.getLoadBalancer();
- System.out.println(lb.getLoadBalancerStats());
- ConfigurationManager.getConfigInstance().setProperty(
- "sample-client.ribbon.listOfServers", "www.linkedin.com:80,www.google.com:80"); // 5
- System.out.println("changing servers ...");
- Thread.sleep(3000); // 6
- for (int i = 0; i < 20; i++) {
- HttpClientResponse response = client.executeWithLoadBalancer(request);
- System.out.println("Status code for " + response.getRequestedURI() + " : " + response.getStatus());
- response.releaseResources();
- }
- System.out.println(lb.getLoadBalancerStats()); // 7
- }
代碼解釋:
- 使用 Archaius ConfigurationManager 加載屬性;
- 使用 ClientFactory 創建客戶端和負載均衡器;
- 使用 builder 構建 http 請求。注意我們只支持 URI 的 "/" 部分的路徑,一旦服務器被負載均衡器選中,會由客戶端計算出完整的 URI;
- 調用 API client.executeWithLoadBalancer(),不是 exeucte() API;
- 動態修正配置中的服務器池;
- 等待服務器列表刷新(配置文件中定義的刷新間隔是爲 2 秒鐘);
- 打印出負載均衡器記錄的服務器統計信息。
關於 Ribbon 客戶端使用的小 demo(包括 LB 調用、動態調整負載均衡節點)請參考博客《Ribbon 和 wowza 的集成開發》。