您好,登錄后才能下訂單哦!
這篇“Ribbon如何使用”文章的知識點大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Ribbon如何使用”文章吧。
Ribbon是一個客戶端負(fù)載均衡工具,封裝Netflix Ribbon組件,能夠提供客戶端負(fù)載均衡能力。
理解Ribbon最重要的就是理解客戶端這個概念,所謂客戶端負(fù)載均衡工具不同于Nginx(服務(wù)端負(fù)載均衡),Ribbon和應(yīng)用程序綁定,本身不是獨立的服務(wù),也不存儲服務(wù)列表,需要負(fù)載均衡的時候,會通過應(yīng)用程序獲取注冊服務(wù)列表,然后通過列表進(jìn)行負(fù)載均衡和調(diào)用。
Nginx獨立進(jìn)程做負(fù)載均衡,通過負(fù)載均衡策略,將請求轉(zhuǎn)發(fā)到不同的服務(wù)上
客戶端負(fù)載均衡,通過在客戶端保存服務(wù)列表信息,然后自己調(diào)用負(fù)載均衡策略,分?jǐn)傉{(diào)用不同的服務(wù)
Ribbon的負(fù)載均衡有兩種方式
和 RestTemplate 結(jié)合 Ribbon+RestTemplate
和 OpenFeign 結(jié)合
Ribbon的核心子模塊
ribbon-loadbalancer:可以獨立使用或者和其他模塊一起使用的負(fù)載均衡API
ribbon-core:Ribbon的核心API
訂單服務(wù)調(diào)用商品服務(wù)
配置過程 分兩步
在訂單服務(wù)中導(dǎo)入ribbon的依賴
<!--ribbon--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
配置 RestTemplate
訂單服務(wù)調(diào)用商品服務(wù)
訂單服務(wù)調(diào)用商品服務(wù)的鏈接 不能寫成ip+端口號,需要寫成商品服務(wù)的服務(wù)名稱
重啟 訂單服務(wù) 測試負(fù)載均衡
Ribbon負(fù)載均衡簡單版實現(xiàn)的流程
RestTemplate發(fā)送的請求是服務(wù)名稱http://nacos-product/product/getProductById/1
獲取
@LoadBalanced
注解標(biāo)記的RestTemplate
RestTemplate
添加一個攔截器,當(dāng)使用RestTemplate
發(fā)起http調(diào)用時進(jìn)行攔截根據(jù)url中的服務(wù)名稱 以及自身的負(fù)載均衡策略 去訂單服務(wù)的服務(wù)列表中找到一個要調(diào)用的ip+端口號 localhost:8802
訪問該目標(biāo)服務(wù),并獲取返回結(jié)果
服務(wù)列表實際上是個map
Ribbon將所有標(biāo)記@LoadBalanced
注解的RestTemplate
保存到一個List集合當(dāng)中,具體源碼如下:
@LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList();
具體源碼位置是在LoadBalancerAutoConfiguration
中。
攔截器不是Ribbon的功能
RestTemplate添加攔截器需要有兩個步驟,首先是定義一個攔截器,其次是將定義的攔截器添加到RestTemplate中。
實現(xiàn)ClientHttpRequestInterceptor
接口就具備了攔截請求的功能,該接口源碼如下:
public interface ClientHttpRequestInterceptor { /** *實現(xiàn)該方法,在該方法內(nèi)完成攔截請求后的邏輯內(nèi)容。 *對于ribbon而言,在該方法內(nèi)完成了根據(jù)具體規(guī)則從 *服務(wù)集群中選取一個服務(wù),并向該服務(wù)發(fā)起請求的操作。 */ ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException; }
ribbon中對應(yīng)的實現(xiàn)類是LoadBalancerInterceptor
具體源碼如下:
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor { private LoadBalancerClient loadBalancer; private LoadBalancerRequestFactory requestFactory; //省略構(gòu)造器代碼... @Override public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException { final URI originalUri = request.getURI(); String serviceName = originalUri.getHost(); /** *攔截請求,并調(diào)用loadBalancer.execute()方法 *在該方法內(nèi)部完成server的選取。向選取的server *發(fā)起請求,并獲得返回結(jié)果。 */ return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution)); } }
RestTemplate
繼承了InterceptingHttpAccessor
,在InterceptingHttpAccessor
中提供了獲取以及添加攔截器的方法,具體源碼如下:
public abstract class InterceptingHttpAccessor extends HttpAccessor { /** * 所有的攔截器是以一個List集合形式進(jìn)行保存。 */ private List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>(); /** * 設(shè)置攔截器。 */ public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) { this.interceptors = interceptors; } /** * 獲取當(dāng)前的攔截器。 */ public List<ClientHttpRequestInterceptor> getInterceptors() { return interceptors; } //省略部分代碼... }
通過這兩個方法我們就可以將剛才定義的LoadBalancerInterceptor
添加到有@LoadBalanced
注解標(biāo)識的RestTemplate
中。具體的源碼如下(LoadBalancerAutoConfiguration)省略部分代碼:
public class LoadBalancerAutoConfiguration { /** * 獲取所有帶有@LoadBalanced注解的restTemplate */ @LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); /** * 創(chuàng)建SmartInitializingSingleton接口的實現(xiàn)類。Spring會在所有 * 單例Bean初始化完成后回調(diào)該實現(xiàn)類的afterSingletonsInstantiated() * 方法。在這個方法中會為所有被@LoadBalanced注解標(biāo)識的 * RestTemplate添加ribbon的自定義攔截器LoadBalancerInterceptor。 */ @Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializer( final List<RestTemplateCustomizer> customizers) { return new SmartInitializingSingleton() { @Override public void afterSingletonsInstantiated() { for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { for (RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); } } } }; } /** * 創(chuàng)建Ribbon自定義攔截器LoadBalancerInterceptor * 創(chuàng)建前提是當(dāng)前classpath下不存在spring-retry。 * 所以LoadBalancerInterceptor是默認(rèn)的Ribbon攔截 * 請求的攔截器。 */ @Configuration @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate") static class LoadBalancerInterceptorConfig { @Bean public LoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) { return new LoadBalancerInterceptor(loadBalancerClient, requestFactory); } /** * 添加攔截器具體方法。首先獲取當(dāng)前攔截器集合(List) * 然后將loadBalancerInterceptor添加到當(dāng)前集合中 * 最后將新的集合放回到restTemplate中。 */ @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } } }
至此知道了ribbon攔截請求的基本原理,接下來我們看看Ribbon是怎樣選取server的。
通過上面的介紹我們知道了當(dāng)發(fā)起請求時ribbon會用LoadBalancerInterceptor
這個攔截器進(jìn)行攔截。在該攔截器中會調(diào)用LoadBalancerClient.execute()
方法,該方法具體代碼如下:
@Override public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException { /** *創(chuàng)建loadBalancer的過程可以理解為組裝選取服務(wù)的規(guī)則(IRule)、 *服務(wù)集群的列表(ServerList)、檢驗服務(wù)是否存活(IPing)等特性 *的過程(加載RibbonClientConfiguration這個配置類),需要注意 *的是這個過程并不是在啟動時進(jìn)行的,而是當(dāng)有請求到來時才會處理。 */ ILoadBalancer loadBalancer = getLoadBalancer(serviceId); /** * 根據(jù)ILoadBalancer來選取具體的一個Server。 * 選取的過程是根據(jù)IRule、IPing、ServerList * 作為參照。 */ Server server = getServer(loadBalancer); if (server == null) { throw new IllegalStateException("No instances available for " + serviceId); } RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server)); return execute(serviceId, ribbonServer, request); }
通過代碼我們可知,首先創(chuàng)建一個ILoadBalancer
,這個ILoadBalancer
是Ribbon的核心類??梢岳斫獬伤诉x取服務(wù)的規(guī)則(IRule
)、服務(wù)集群的列表(ServerList
)、檢驗服務(wù)是否存活(IPing
)等特性,同時它也具有了根據(jù)這些特性從服務(wù)集群中選取具體一個服務(wù)的能力。 Server server = getServer(loadBalancer);
這行代碼就是選取舉一個具體server。 最終調(diào)用了內(nèi)部的execute
方法,該方法代碼如下(只保留了核心代碼):
@Override public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException { try { //發(fā)起調(diào)用 T returnVal = request.apply(serviceInstance); statsRecorder.recordStats(returnVal); return returnVal; } catch (IOException ex) { statsRecorder.recordStats(ex); throw ex; } catch (Exception ex) { statsRecorder.recordStats(ex); ReflectionUtils.rethrowRuntimeException(ex); } return null; }
接下來看下request.apply(serviceInstance)
方法的具體做了那些事情(LoadBalancerRequestFactory中):
@Override public ClientHttpResponse apply(final ServiceInstance instance) throws Exception { HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer); //省略部分代碼... /** * 發(fā)起真正請求。 */ return execution.execute(serviceRequest, body); }
看到這里整體流程的原理就說完了,接下來我們結(jié)合一張圖來回顧下整個過程:
首先獲取所有標(biāo)識@LoadBalanced
注解的RestTemplate
(可以理解成獲取那些開啟了Ribbon負(fù)載均衡功能的RestTemplate
),然后將Ribbon默認(rèn)的攔截器LoadBalancerInterceptor
添加到RestTemplate
中,這樣當(dāng)使用RestTemplate
發(fā)起http請求時就會起到攔截的作用。當(dāng)有請求發(fā)起時,ribbon默認(rèn)的攔截器首先會創(chuàng)建ILoadBalancer
(里面包含了選取服務(wù)的規(guī)則(IRule
)、服務(wù)集群的列表(ServerList
)、檢驗服務(wù)是否存活(IPing
)等特性)。在代碼層面的含義是加載RibbonClientConfiguration
配置類)。然后使用ILoadBalancer
從服務(wù)集群中選擇一個服務(wù),最后向這個服務(wù)發(fā)送請求。
參考資料:https://www.jianshu.com/p/79b9cf0d0519
根據(jù)上述Ribbon的原理,可以知道IRule接口負(fù)責(zé)負(fù)載均衡的實現(xiàn),具體如下:
規(guī)則名稱 特點 AvailabilityFilteringRule 過濾掉一直連接失敗的被標(biāo)記為circuit tripped的后端Server,并 過濾掉那些高并發(fā)的后端Server或者使用一個AvailabilityPredicate 來包含過濾server的邏輯,其實就是檢查status里記錄的各個server 的運行狀態(tài) BestAvailableRule 選擇一個最小的并發(fā)請求的server,逐個考察server, 如果Server被tripped了,則跳過 RandomRule 隨機選擇一個Server ResponseTimeWeightedRule 已廢棄,作用同WeightedResponseTimeRule WeightedResponseTimeRule 權(quán)重根據(jù)響應(yīng)時間加權(quán),響應(yīng)時間越長,權(quán)重越小,被選中的可能性越低 RetryRule 對選定的負(fù)載均衡策略加上重試機制,在一個配置時間段內(nèi)當(dāng) 選擇Server不成功,則一直嘗試使用subRule的方式選擇一個 可用的Server RoundRobinRule 輪詢選擇,輪詢index,選擇index對應(yīng)位置的Server ZoneAvoidanceRule 默認(rèn)的負(fù)載均衡策略,即復(fù)合判斷Server所在區(qū)域的性能和Server的可用性 選擇Server,在沒有區(qū)域的環(huán)境下,類似于輪詢(RandomRule) 其中RandomRule表示隨機策略、RoundRobinRule表示輪詢策略、WeightedResponseTimeRule表示加權(quán)策略、BestAvailableRule表示請求數(shù)最少策略等等
隨機源碼:
輪詢源碼:
默認(rèn)是輪詢 可以修改為任意的規(guī)則
修改為隨機算法
創(chuàng)建具有負(fù)載均衡功能的RestTemplate實例
[@Bean](https://my.oschina.net/bean) @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }
使用RestTemplate進(jìn)行rest操作的時候,會自動使用負(fù)載均衡策略,它內(nèi)部會在RestTemplate中加入LoadBalancerInterceptor這個攔截器,這個攔截器的作用就是使用負(fù)載均衡。
默認(rèn)情況下會采用輪詢策略,如果希望采用其它策略,則指定IRule實現(xiàn),如:
[@Bean](https://my.oschina.net/bean) public IRule ribbonRule() { return new BestAvailableRule(); }
這種方式對OpenFeign也有效。
修改為按照Nacos配置的權(quán)重進(jìn)行負(fù)載均衡
在nacos中對集群進(jìn)行權(quán)重的配置
在項目中,選擇使用 NacosRule
Ribbon默認(rèn)懶加載,意味著只有在發(fā)起調(diào)用的時候才會創(chuàng)建客戶端
ribbon: eager-load: # 開啟ribbon饑餓加載 enabled: true # 配置user-center使用ribbon饑餓加載,多個使用逗號分隔 clients: user-center
主要調(diào)整請求的超時時間,是否重試
如果業(yè)務(wù)沒有做冪等性的話建議把重試關(guān)掉:ribbon.MaxAutoRetriesNextServer=0
# 從注冊中心刷新servelist的時間 默認(rèn)30秒,單位ms ribbon.ServerListRefreshInterval=15000 # 請求連接的超時時間 默認(rèn)1秒,單位ms ribbon.ConnectTimeout=30000 # 請求處理的超時時間 默認(rèn)1秒,單位ms ribbon.ReadTimeout=30000 # 對所有操作請求都進(jìn)行重試,不配置這個MaxAutoRetries不起作用 默認(rèn)false #ribbon.OkToRetryOnAllOperations=true # 對當(dāng)前實例的重試次數(shù) 默認(rèn)0 # ribbon.MaxAutoRetries=1 # 切換實例的重試次數(shù) 默認(rèn)1 ribbon.MaxAutoRetriesNextServer=0
如果
MaxAutoRetries=1
和MaxAutoRetriesNextServer=1
請求在1s內(nèi)響應(yīng),超過1秒先同一個服務(wù)器上重試1次,如果還是超時或失敗,向其他服務(wù)上請求重試1次。那么整個ribbon請求過程的超時時間為:ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1)
以上就是關(guān)于“Ribbon如何使用”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。