javascript
Spring-Cloud 学习笔记-(4)负载均衡器Ribbon
目錄
- Spring-Cloud 學(xué)習(xí)筆記-(4)負(fù)載均衡器Ribbon
- 1、前言
- 2、什么是負(fù)載均衡
- 2.1、問題分析
- 2.2、什么是Ribbon
- 3、快速入門
- 3.1、實(shí)現(xiàn)方式一
- 3.1.1、修改代碼
- 3.2、實(shí)現(xiàn)方式二
- 3.2.1、啟動(dòng)類
- 3.2.2、調(diào)用代碼
- 3.2.3、測(cè)試
- 3.2.4、實(shí)現(xiàn)原理
- 3.2.5、斷點(diǎn)調(diào)式
- 3.3、修改輪詢策略
- 3.4、重載機(jī)制
- 3.4.1、為什么要有重載機(jī)制
- 3.4.2、實(shí)現(xiàn)代碼
- 3.1、實(shí)現(xiàn)方式一
Spring-Cloud 學(xué)習(xí)筆記-(4)負(fù)載均衡器Ribbon
1、前言
上個(gè)章節(jié)我們做了什么?
上個(gè)章節(jié)我們說(shuō)了用eureka來(lái)實(shí)現(xiàn)服務(wù)的注冊(cè)與發(fā)現(xiàn),并且用過服務(wù)的seviceId拉取了服務(wù)列表List<ServiceInstance> instances = discoveryClient.getInstances("user-service") 從而實(shí)現(xiàn)服務(wù)的調(diào)用方(order-service)調(diào)用服務(wù)的提供方(user-service)
本章節(jié)我們會(huì)做什么?
負(fù)載均衡
2、什么是負(fù)載均衡
2.1、問題分析
? 上個(gè)章節(jié)我們實(shí)現(xiàn)了服務(wù)之間的調(diào)用,把原來(lái)代碼里面寫死的ip地址,換成用serviceId拉取服務(wù)列表,然后從服務(wù)列表中獲取實(shí)例的方式,雖然代碼變得復(fù)雜了,但是思想上我們得到的升級(jí),但是還是存在一個(gè)問題,就是ServiceInstance instance = instances.get(0);每次我們都取同一個(gè)ip,要想每次使用不同的ip,我們自己就要寫負(fù)載均衡算法,從多個(gè)實(shí)例當(dāng)中獲取某一個(gè)實(shí)例進(jìn)行調(diào)用,這次我們這一章節(jié)講的就是負(fù)載均衡器Ribbon,它里面內(nèi)置了很多負(fù)載均衡的算法,幫我們從實(shí)例列表中獲取一個(gè)實(shí)例。
2.2、什么是Ribbon
3、快速入門
我們按照上節(jié)課方法啟動(dòng),一個(gè)Eureka注冊(cè)中心,兩個(gè)(方便演示負(fù)載均衡)服務(wù)的提供方(user-service),最后我們修改服務(wù)調(diào)用方(order-service)代碼
pom文件
<!-- ribbon --> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>3.1、實(shí)現(xiàn)方式一
3.1.1、修改代碼
//注入 RibbonLoadBalancerClient@Autowiredprivate LoadBalancerClient client; ...//通過serviceId 拉取服務(wù)列表//List<ServiceInstance> instances = discoveryClient.getInstances("user-service");//ServiceInstance instance = instances.get(0);ServiceInstance instance = client.choose("user-service"); String jsonStr = restTemplate.getForObject("http://"+instance.getHost()+":"+instance.getPort()+"/api/v1/user/2", String.class);之前我們是通過serviceId(“user-service”)獲取到的是服務(wù)列表,現(xiàn)在我們直接可以通過serviceId,返回的是單個(gè)實(shí)例,不是因?yàn)榱斜砝锩嬷挥幸粋€(gè)實(shí)例,是因?yàn)閏hoose方法中已經(jīng)幫我們做了復(fù)雜均衡了。
3.2、實(shí)現(xiàn)方式二
3.2.1、啟動(dòng)類
/*** 把RestTemplate注入到Spring容器中*/ @Bean @LoadBalanced //增加注解 讓RestTemplate內(nèi)置一個(gè)負(fù)載均衡器 public RestTemplate restTemplate(){return new RestTemplate(); }3.2.2、調(diào)用代碼
String jsonStr = restTemplate.getForObject("http://user-service/api/v1/user/2", String.class);之前我們把serviceId交出去,別人幫我們?nèi)∫欢?#xff0c;或者取一個(gè),現(xiàn)在直接把serviceId寫在url路徑中。
3.2.3、測(cè)試
啟動(dòng)order-service
訪問:http://localhost:8781/api/v1/order/2
3.2.4、實(shí)現(xiàn)原理
其實(shí)實(shí)現(xiàn)方式二低層就是用的實(shí)現(xiàn)方式一,只不過實(shí)現(xiàn)方式二加了一個(gè)攔截器LoadBalancerInterceptor對(duì)RestTemplate請(qǐng)求做了攔截,然后把請(qǐng)求路徑中的serviceId("user-service")拿到,然后通過方式一獲取某一個(gè)實(shí)例進(jìn)行調(diào)用。
3.2.5、斷點(diǎn)調(diào)式
我們按兩下shift搜索到LoadBalancerInterceptor這個(gè)攔截器,在intercept方法中打好斷點(diǎn)
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {//1.拿到請(qǐng)求路徑URI originalUri = request.getURI();//2.從路徑中獲取serviceIdString serviceName = originalUri.getHost();Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);//3.執(zhí)行execute方法.. 我們接著看execute方法return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution)); }// RibbonLoadBalancerClient類中 public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {//4.根據(jù)serviceId獲取到負(fù)載均衡器ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);//5.通過負(fù)載均衡器拿到某一個(gè)實(shí)例..我們接著看getServer方法Server server = this.getServer(loadBalancer);if (server == null) {throw new IllegalStateException("No instances available for " + serviceId);} else {RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));return this.execute(serviceId, ribbonServer, request);}}//RibbonLoadBalancerClient類中 protected Server getServer(ILoadBalancer loadBalancer) {//6.判斷負(fù)載均衡器是不是為空,不為空調(diào)用chooseServer("default")方法,我們接著看return loadBalancer == null ? null : loadBalancer.chooseServer("default"); }//BaseLoadBalancer類中 public Server chooseServer(Object key) {//這里key是默認(rèn)值“default”if (this.counter == null) {this.counter = this.createCounter();}this.counter.increment();if (this.rule == null) {return null;} else {try {//根據(jù)rule調(diào)用choose方法,其中IRule是一個(gè)接口,有很多實(shí)現(xiàn)類,每一個(gè)實(shí)現(xiàn)類對(duì)于不同的負(fù)載均衡策略,比如RandomRule隨機(jī),RoundRobinRule輪詢等,我們BaseLoadBalancer類中有一個(gè)屬性, private static final IRule DEFAULT_RULE = new RoundRobinRule();代表默認(rèn)輪詢策略,有興趣的可以看一下每一個(gè)實(shí)現(xiàn)的choose方法,比如輪詢策略RoundRobinRule,底層維護(hù)一個(gè)自增長(zhǎng)的count,每調(diào)用一次count++,然后每次用count模于服務(wù)列表的長(zhǎng)度(比如第一次:1%5=1,第二次:2%5=2)得到的值為服務(wù)列表的位置索引,從而實(shí)現(xiàn)輪詢。return this.rule.choose(key);} catch (Exception var3) {logger.warn("LoadBalancer [{}]: Error choosing server for key {}", new Object[]{this.name, key, var3});return null;}}}3.3、修改輪詢策略
application.yml:
#--負(fù)載均衡輪詢策略 # serviceId user-service:ribbon:#負(fù)載均衡策略的classNameNFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule3.4、重載機(jī)制
3.4.1、為什么要有重載機(jī)制
在上一章中我們說(shuō)到,一個(gè)正常的eureka客戶端,每間隔30秒沒有給服務(wù)器發(fā)送心跳,如果90秒服務(wù)器還沒有收到心跳,服務(wù)器就會(huì)認(rèn)為這個(gè)客戶端已經(jīng)宕機(jī),但是eureka不會(huì)馬上剔除,沒間隔60會(huì)同意剔除這些失效的客戶端,這樣導(dǎo)致,我們服務(wù)實(shí)際上已經(jīng)宕機(jī)了但是服務(wù)列表里面還有,這樣服務(wù)的消費(fèi)者在調(diào)用的時(shí)候就會(huì)報(bào)錯(cuò),假設(shè)我們服務(wù)的提供方有五個(gè),雖然只宕機(jī)了一臺(tái),但是還有四臺(tái)是正常的,在這個(gè)時(shí)候我們條用這個(gè)服務(wù)如果出現(xiàn)報(bào)錯(cuò)信息肯定不是我們希望看到的,所有我們就有了這個(gè)重載機(jī)制,Spring Cloud 整合了Spring Retry 來(lái)增強(qiáng)RestTemplate的重試能力,當(dāng)一次服務(wù)調(diào)用失敗后,不會(huì)立即拋出一次,而是再次重試下一個(gè)服務(wù)。
3.4.2、實(shí)現(xiàn)代碼
application.yml
#服務(wù)名稱 spring:application:name: order-servicecloud:loadbalancer:retry:enabled: true # 開啟Spring Cloud的重試功能#負(fù)載均衡輪詢策略 user-service:ribbon:#負(fù)載均衡策略的classNameNFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRuleConnectTimeout: 250 # Ribbon的連接超時(shí)時(shí)間ReadTimeout: 1000 # Ribbon的數(shù)據(jù)讀取超時(shí)時(shí)間OkToRetryOnAllOperations: true # 是否對(duì)所有操作都進(jìn)行重試MaxAutoRetriesNextServer: 1 # 切換實(shí)例的重試次數(shù)MaxAutoRetries: 1 # 對(duì)當(dāng)前實(shí)例的重試次數(shù)我們測(cè)試發(fā)現(xiàn),就算我們user-service宕機(jī)了,也能通過另一臺(tái)服務(wù)實(shí)例獲取到結(jié)果!
轉(zhuǎn)載于:https://www.cnblogs.com/bigfly277/p/10147083.html
總結(jié)
以上是生活随笔為你收集整理的Spring-Cloud 学习笔记-(4)负载均衡器Ribbon的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 30分钟彻底弄懂flex布局
- 下一篇: Spring Boot 自动配置原理