Spring Cloud Ribbon
簡介
Spring Cloud Ribbon是一個基於HTTP和TCP的客戶端負載均衡工具,它基於Netflix Ribbon實現。通過Spring Cloud的封裝,可以讓我們輕鬆地將面向服務的REST模版請求自動轉換成客戶端負載均衡的服務呼叫。Spring Cloud Ribbon雖然只是一個工具類框架,它不像服務註冊中心、配置中心、API閘道器那樣需要獨立部署,但是它幾乎存在於每一個Spring Cloud構建的微服務和基礎設施中。因為微服務間的呼叫,API閘道器的請求轉發等內容,實際上都是通過Ribbon來實現的,包括後續我們將要介紹的Feign,它也是基於Ribbon實現的工具。所以,對Spring Cloud Ribbon的理解和使用,對於我們使用Spring Cloud來構建微服務非常重要。Ribbon預設為我們提供了很多負載均衡演算法,例如輪詢、隨機等。當然,我們也可為Ribbon實現自定義的負載均衡演算法。
Ribbon的負載均衡應用在以下幾方面:
- RestTemplate
- Feign
- Zuul
Ribbon實現軟負載均衡有三點:
-
服務發現
發現依賴服務的列表,依據服務的名字,把該服務所有的例項找到。
-
服務選擇規則
依據規則策略,如何從多個服務中選擇一個有效的服務
-
服務監聽
檢測失效的服務,做到高效剔除
Ribbon的主要元件包括:
- ServerList
- IRule
- ServerListFilter
首先通過ServerList獲取所有可用服務列表,然後通過ServerListFilter過濾掉一部分地址,最後剩下的地址中,使用IRule選擇一個例項作為最終目標結果。
負載均衡有好幾種實現策略,常見的有:
- 隨機 (Random)
- 輪詢 (RoundRobin)
- 一致性雜湊 (ConsistentHash)
- 雜湊 (Hash)
- 加權(Weighted)
ILoadBalance 負載均衡器
Ribbon內部提供了一個叫做ILoadBalance的介面代表負載均衡器的操作,其中包括新增伺服器操作、選擇伺服器操作、獲取所有的伺服器列表、獲取可用的伺服器列表等。
ILoadBalance的繼承關係如下:
負載均衡器是從EurekaClient(EurekaClient的實現類為DiscoveryClient)獲取服務資訊,根據IRule去路由,並且根據IPing判斷服務的可用性。在BaseLoadBalancer類下,BaseLoadBalancer的建構函式,該建構函式開啟了一個PingTask任務setupPingTask();,程式碼如下:
public BaseLoadBalancer(String name, IRule rule, LoadBalancerStats stats,
IPing ping, IPingStrategy pingStrategy) {
logger.debug("LoadBalancer [{}]: initialized", name);
this.name = name;
this.ping = ping;
this.pingStrategy = pingStrategy;
setRule(rule);
setupPingTask();
lbStats = stats;
init();
}
setupPingTask方法開啟了ShutdownEnabledTimer執行PingTask任務,在預設情況下pingIntervalSeconds為10,即每10秒鐘,向EurekaClient傳送一次”ping”,並有可能從Eureka Client獲取註冊資訊。針對PingTask,它根據pingerStrategy.pingServers(ping, allServers)來獲取服務的可用性,如果該返回結果,如之前相同,則不去向EurekaClient獲取註冊列表,如果不同則通知ServerStatusChangeListener或者changeListeners發生了改變,進行更新或者重新拉取。
class PingTask extends TimerTask {
public void run() {
try {
new Pinger(pingStrategy).runPinger();
} catch (Exception e) {
logger.error("LoadBalancer [{}]: Error pinging", name, e);
}
}
}
void setupPingTask() {
if (canSkipPing()) {
return;
}
if (lbTimer != null) {
lbTimer.cancel();
}
lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + name,true);
lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000);
forceQuickPing();
}
如果可用性改變後,重新拉取了註冊中心的資料,LoadBalancerClient有了這些服務註冊的新列表,就可以根據具體的IRule來進行負載均衡。
IRule 路由
IRule介面代表負載均衡策略:
public interface IRule{
/*
* choose one alive server from lb.allServers or
* lb.upServers according to key
*
* @return choosen Server object. NULL is returned if none
* server is available
*/
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
IRule的實現關係如下:
自定義負責均衡策略
策略類 | 命名 | 說明 |
---|---|---|
RandomRule | 隨機策略 | 隨機選擇 Server |
RoundRobinRule | 輪訓策略 | 按順序迴圈選擇 Server |
RetryRule | 重試策略 | 在一個配置時問段內當選擇 Server 不成功,則一直嘗試選擇一個可用的 Server |
BestAvailableRule | 最低併發策略 | 逐個考察 Server,如果 Server 斷路器開啟,則忽略,再選擇其中併發連線最低的 Server |
AvailabilityFilteringRule | 可用過濾策略 | 過濾掉一直連線失敗並被標記為 circuit tripped 的 Server,過濾掉那些高併發連線的 Server(active connections 超過配置的網值) |
ResponseTimeWeightedRule | 響應時間加權策略 | 根據 Server 的響應時間分配權重。響應時間越長,權重越低,被選擇到的概率就越低;響應時間越短,權重越高,被選擇到的概率就越高。這個策略很貼切,綜合了各種因素,如:網路、磁碟、IO等,這些因素直接影響著響應時間 |
ZoneAvoidanceRule | 區域權衡策略 | 綜合判斷 Server 所在區域的效能和 Server 的可用性輪詢選擇 Server,並且判定一個 AWS Zone 的執行效能是否可用,剔除不可用的 Zone 中的所有 Server |
全域性配置
-
配置類方式
@Configuration
public class RibbonGlobalLoadBalancingConfiguration {
/**
* 隨機規則
*/
@Bean
public IRule ribbonRule() {
return new RandomRule();
}
}
指定服務配置
-
註解方式
增加一個針對單個服務的 Ribbon 負載聚恆策略配置類:
@Configuration
//標記使用的註解
@AvoidScan
public class RibbonRandomLoadBalancingConfiguration {
//針對客戶端的配置管理器。
@Resource
IClientConfig clientConfig;
@Bean
public IRule ribbonRule(IClientConfig clientConfig) {
return new RandomRule();
}
}
啟動類上加入針對單個服務的負載均衡策略:
/** 配置針對單個服務的 Ribbon 負載均衡策略 **/
@RibbonClient(
name = "users", configuration = RibbonRandomLoadBalancingConfiguration.class
)
/** 此處配置根據標識 @AvoidScan 過濾掉需要單獨配置的 Ribbon 負載均衡策略,不然就會作用於全域性,啟動就會報錯 */
@ComponentScan(
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION, value = AvoidScan.class
)
)
public class UsersApplication {
public static void main(String[] args) {
SpringApplication.run(UsersApplication.class, args);
}
}
-
配置檔案方式(推薦)
application.yml.
users:#服務名稱
ribbon:
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule