1.概念
ribbon是一款客戶端負載均衡器,用於微服務之間的負載均衡。
首先,什麼是客戶端負載均衡?
如圖,ribbon可以透過註冊中心獲取服務列表,然後自己執行自己的負載均衡策略來決定要訪問哪個微服務,這就是客戶端負載均衡,選擇的主導權在客戶端自己手裡。
區別於服務端的負載均衡,客戶端的負載均衡可以由客戶端自己選擇。
例如你去食堂吃飯,服務端的負載均衡就是食堂自動給你分配伙食,你沒得選。而客戶端的負載均衡就是進入食堂,你有一個菜譜,你可以按照菜譜自己選擇要吃的伙食。
2.用一段虛擬碼來實現ribbon的客戶端代理
透過重寫RestTemplate 的doExecute方法,來完成客戶端的負載均衡。這裡的策略是隨機一個。
@Slf4jpublic class MyRestTemplate extends RestTemplate { private DiscoveryClient discoveryClient; public MyRestTemplate(DiscoveryClient discoveryClient) { this。discoveryClient = discoveryClient; } protected
3.透過Ribbon元件來實現負載均衡
第一步:依賴
<!—— 加入nocas‐client——>
第二步 配置RestTemplate
@LoadBalanced@Beanpublic RestTemplate restTemplate() { return new RestTemplate();}
這樣就可以使用ribbon了。
4.原始碼解析
☐
LoadBalancerAutoConfiguration 這個類配置了ribbon對restTemplate的整合
@Configuration(proxyBeanMethods = false)@ConditionalOnClass(RestTemplate。class)@ConditionalOnBean(LoadBalancerClient。class)@EnableConfigurationProperties(LoadBalancerRetryProperties。class)public class LoadBalancerAutoConfiguration { @LoadBalanced @Autowired(required = false) private List
4.1首先,這個類維護了一個RestTemplate的列表,並且透過RestTemplateCustomizer對這些RestTemplate新增一個攔截器;
// 遍歷新增RestTemplateCustomizer@Beanpublic SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated( final ObjectProvider> restTemplateCustomizers) { return () -> restTemplateCustomizers。ifAvailable(customizers -> { for (RestTemplate restTemplate : LoadBalancerAutoConfiguration。this。restTemplates) { for (RestTemplateCustomizer customizer : customizers) { customizer。customize(restTemplate); } } });}
@Configuration(proxyBeanMethods = false)@ConditionalOnMissingClass(“org。springframework。retry。support。RetryTemplate”)static class LoadBalancerInterceptorConfig { // 攔截器註冊bean @Bean public LoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) { return new LoadBalancerInterceptor(loadBalancerClient, requestFactory); } // 新增聯結器 @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return restTemplate -> { List
4.2其次,RestTemplateCustomizer 又透過LoadBalancerInterceptor 進行介面攔截,而攔截器的主要就是把serviceId取出來,再使用負載均衡器發起http請求。
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor { private LoadBalancerClient loadBalancer; private LoadBalancerRequestFactory requestFactory; …… @Override public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException { // url獲取 final URI originalUri = request。getURI(); String serviceName = originalUri。getHost(); // 執行 return this。loadBalancer。execute(serviceName, this。requestFactory。createRequest(request, body, execution)); }}
4.3然後,在RibbonLoadBalancerClient類裡面進行負載均衡的操作
public
@Overridepublic
4.4最後在AsyncLoadBalancerInterceptor物件裡面的intercept方法進行http請求
public ListenableFuture
4.5那麼ribbon怎麼獲取服務呢?回到步驟4.3,這裡透過getServer()方法呼叫ILoadBalancer這一介面的實現類來實現的
protected Server getServer(ILoadBalancer loadBalancer, Object hint) { if (loadBalancer == null) { return null; } //選擇服務,沒有就預設 return loadBalancer。chooseServer(hint != null ? hint : “default”);}
public interface ILoadBalancer { // 初始化服務列表 public void addServers(List
選擇的最終在BaseLoadBalancer裡面,預設選擇的負載均衡策略為RoundRobinRule
private final static IRule DEFAULT_RULE = new RoundRobinRule();protected IRule rule = DEFAULT_RULE;// 沒有自定義的rule就選預設的public Server chooseServer(Object key) { if (counter == null) { counter = createCounter(); } counter。increment(); if (rule == null) { return null; } else { try { return rule。choose(key); } catch (Exception e) { logger。warn(“LoadBalancer [{}]: Error choosing server for key {}”, name, key, e); return null; } }}
4.6 ribbon中已經有的負載均衡策略
①:RandomRule
(隨機選擇一個Server)
②:RetryRule
對選定的負載均衡策略機上重試機制,在一個配置時間段內當選擇Server不成功, 則一直嘗試使用subRule的方式選擇一個可用的server。
③:RoundRobinRule
輪詢選擇, 輪詢index,選擇index對應位置的Server
④:AvailabilityFilteringRule
過濾掉一直連線失敗的被標記為circuit tripped的後端Server,並過濾掉那些高併發的後端 Server或者使用一個AvailabilityPredicate來包含過濾server的邏輯,其實就就是檢查 status裡記錄的各個Server的執行狀態
⑤:BestAvailableRule
選擇一個最小的併發請求的Server,逐個考察Server,如果Server被tripped了,則跳過。
⑥:WeightedResponseTimeRule
根據響應時間加權,響應時間越長,權重越小,被選中的可能性越低;
⑦:ZoneAvoidanceRule
複合判斷Server所在Zone的效能和Server的可用性選擇Server,在沒有Zone的情況下類是 輪詢。
5.總結
ribbon原理和程式碼不難,照著步驟多看看就懂了。