type
status
date
urlname
summary
tags
category
icon
password
catalog
sort
RestTemplate
在
SpringCloud
体系中,我们知道服务之间的调用是通过http
协议进行调用的。而注册中心的主要目的就是维护这些服务的服务列表。我们知道,在Spring
中,提供了RestTemplate
。RestTemplate
是Spring
提供的用于访问Rest服务的客户端。而在SpringCloud
中也是使用此服务进行服务调用的。同时在微服务中,一般上服务都不会进行单点部署的,都会至少部署2台及以上的。现在我们有了注册中心进行服务列表的维护,就需要一个客户端负载均衡来进行动态服务的调用。
所以开始示例前,我们先来大致了解下关于
负载均衡
和RestTemplate
的相关知识点。其实后面实例的Ribbon
和Feign
最后的调用都是基于RestTemplate
的。使用比较简单~何为负载均衡
负载均衡(Load Balance)是分布式系统架构设计中必须考虑的因素之一,它通常是指,将请求/数据**【均匀】分摊**到多个操作单元上执行,负载均衡的关键在于【均匀】。
实现的方式
实现负载均衡的方式有很多种,这里简单介绍下几种方式,并未过多深入。
注意:以下部分内容转至几种负载均衡技术的实现。
1.HTTP重定向负载均衡
根据用户的http请求计算出一个真实的web服务器地址,并将该web服务器地址写入http重定向响应中返回给浏览器,由浏览器重新进行访问
优缺点:实现起来很简单,而缺点也显而易见了:请求两次才能完成一次访问;性能差;重定向服务器会成为瓶颈
2.DNS域名解析负载均衡
在DNS服务器上配置多个域名对应IP的记录。例如一个域名www.baidu.com对应一组web服务器IP地址,域名解析时经过DNS服务器的算法将一个域名请求分配到合适的真实服务器上。
优缺点:加快访问速度,改善性能。同时由于DNS解析是多级解析,每一级DNS都可能化缓存记录A,当某一服务器下线后,该服务器对应的DNS记录A可能仍然存在,导致分配到该服务器的用户访问失败,而且DNS负载均衡采用的是简单的轮询算法,不能区分服务器之间的差异,不能反映服务器当前运行状态。
3.反向代理负载均衡
反向代理处于web服务器这边,反向代理服务器提供负载均衡的功能,同时管理一组web服务器,它根据负载均衡算法将请求的浏览器访问转发到不同的web服务器处理,处理结果经过反向服务器返回给浏览器。
优缺点:实现简单,可利用反向代理缓存资源(这是最常用的了)及改善网站性能。同时因为是所有请求和响应的中转站,所以反向代理服务器可能成为瓶颈。
以上仅仅是部分实现方式,还有比如
IP负载均衡
、数据链路层负载均衡
等等,这些可能涉及到相关网络方面的知识点了,不是很了解,大家有兴趣可以自行搜索下吧。客户端和服务端的负载均衡
实现负载均衡也又区分客户端和服务端之分,
Ribbon
就是基于客户端的负载均衡。
客户端负载均衡:服务端负载均衡:
服务端实现负载均衡方式有很多,比如:
硬件F5
、Nginx
、HA Proxy
等等,这些应该实施相关人员应该比较熟悉了,本人可能也就对Nginx
了解下,⊙﹏⊙‖∣RestTemplate简单介绍
RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。
简单来说,
RestTemplate
采用了模版设计
的设计模式,将过程中与特定实现相关的部分委托给接口,而这个接口的不同实现定义了接口的不同行为,所以可以很容易的使用不同的第三方http服务,如okHttp
、httpclient
等。RestTemplate
定义了很多的与REST资源交互,这里简单介绍下一些常用的请求方式的使用。exchange
在URL上执行特定的HTTP方法,返回包含对象的
ResponseEntity
。其他的如GET
、POST
等方法底层都是基于此方法的。如:
- get请求
- post请求
GET请求
get请求可以分为两类:getForEntity() 和 getForObject().
其他的方法都大同小异了,可以根据实际的业务需求进行调用。
POST请求
简单示例:
关于
postForLocation()
,用的比较少,作用是返回新创建资源的URI,前面介绍的两者是返回资源本身,也就是结果集了。关于其他的请求类型相关用法,这里就不详细阐述了,都是类似的。可以查看下此文章:详解 RestTemplate 操作,讲的蛮详细了。
- *特别说明:系列教程为了方便,github上分别创建了一个单体的
Eureka
注册中心和高可用的Eureka
注册中心,无特殊说明,都是使用单体的Eureka
注册中心进行服务注册与发现的,工程名为:spring-cloud-eureka-server
,端口号为:1000。服务提供方工程名为:spring-cloud-eureka-client
,应用名称为:eureka-client
,端口号为:2000,提供了一个接口:http://127.0.0.1:2000/hello**
spring-cloud-eureka-server示例:spring-cloud-eureka-server
spring-cloud-eureka-client示例:spring-cloud-eureka-client
LoadBalancerClient实例
此类是实现客户端负载均衡的关键。本身它是个接口类,位于
spring-cloud-commons
包下,此包包含了大量的服务治理相关的抽象接口,比如已经介绍过的DiscoveryClient
、ServiceRegistry
以及LoadBalancerClient实例
等等。首先,我们使用最原生的方式去获取调用服务接口。
创建个工程:
spring-cloud-eureka-consumer
0.引入pom文件依赖。
1.配置文件添加相关注册中心等信息。
2.编写启动类,加入
@EnableDiscoveryClient
,申明为一个客户端应用,同时申明一个RestTemplate
,最后是使用RestTemplate
来完成rest服务调用的。3.编写一个调用类,调用
spring-cloud-eureka-client
服务提供者提供的服务。4.启动应用,访问:http://127.0.0.1:8008/hell0?name=oKong ,可以看见控制台输出了利用
LoadBalancerClient
的choose
方法,获取到了对应eureka-client
服务ID的服务地址。最后通过范围对应的http地址进行服务请求:
最后浏览器上可以看见,进行了正确的访问了:
此时,切换到服务提供者
控制台,可以看见日志输出:
此时我们已经调用成功了,通过
LoadBalancerClient
获取到了服务提供者实际服务地址,最后进行调用。大家可以创建多个的
spring-cloud-eureka-client
服务提供者,再去调用下,可以看见会调用不同的服务地址的。客户端负载均衡Ribbon实例
Spring Cloud Ribbon是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的。与Eureka配合使用时,Ribbon可自动从Eureka Server (注册中心)获取服务提供者地址列表,并基于负载均衡算法,通过在客户端中配置ribbonServerList来设置服务端列表去轮询访问以达到均衡负载的作用。
上小节,简单的使用
LoadBalancerClient
进行了服务实例获取最后调用,也说了其实LoadBalancerClient
是个接口类。而Ribbon
实现了此接口,对应实现类为:RibbonLoadBalancerClient
.Ribbon实例
现在我们来看下,使用
Ribbon
的方式如何进行更加优雅的方式进行服务调用。创建一个工程:
spring-cloud-eureka-consumer-ribbon
(其实这个工程和spring-cloud-eureka-consumer
是差不多的,只是有些许不同。)0.加入pom依赖
1.配置文件修改,添加注册中心等相关信息。
2.编写启动类,加入
@EnableDiscoveryClient
,同时申明一个RestTemplate
,这里和原先不同,就在于加入了@LoadBalanced
注解进行修饰RestTemplate
类,稍后会大致讲解下是如何进行实现的。3.编写测试类,进行服务调用。
可以看见,可以直接注入
RestTemplate
,通过服务名直接调用.4.启动应用,访问:http://127.0.0.1:8018/hello?name=oKong ,可以看见调用成功:
控制台输出:
简单聊聊LoadBalanced注解
可以从以上示例中,可以看出,我们就加了一个@LoadBalanced注解修饰RestTemplatebean类,就实现了服务的调用。现在来简单看看具体是如何实现的。
首先,我们看看此注解的代码说明:
从注释可以看出,该注解用来给RestTemplate做标记,以使用负载均衡的客户端
LoadBalancerClient
。现在来看一眼相同包下的类的情况,可以看到有个
LoadBalancerAutoConfiguration
,字面意思可以知道这是一个自动配置类,此类就是我们要找的关键类了。LoadBalancerAutoConfiguration
,此类不长,一百来行,这里就不贴了。简单说明下:
首先,此类生效的条件是
RestTemplate
类必须存在于当前工程的环境中。
- 在Spring的Bean工程中有必须有
LoadBalancerClient
的实现Bean。
该自动化配置类中,主要做了几件事情:
- 维护了一个被@LoadBalanced注解修饰的RestTemplate对象列表
同时为其每个对象通过调用
RestTemplateCustomizer
添加了一个LoadBalancerInterceptor
和RetryLoadBalancerInterceptor
拦截器(有生效条件),其为ClientHttpRequestInterceptor
接口的实现类,ClientHttpRequestInterceptor
是RestTemplate
的请求拦截器RetryLoadBalancerInterceptor拦截器
LoadBalancerInterceptor拦截器
我们主要看下
LoadBalancerInterceptor
:可以看见,最后是实现了
ClientHttpRequestInterceptor
接口的实现类执行execute
方法进行.从继承关系里,此实现类就是
RibbonLoadBalancerClient
类了。类:
简单来说:最后还是通过
loadBalancerClient.choose()
获取到服务实例,最通过拼凑http地址来进行最后的服务调用。总体来说,就是通过为加入
@LoadBalanced
注解的RestTemplate
添加一个请求拦截器,在请求前通过拦截器获取真正的请求地址,最后进行服务调用。里面的细节就不阐述了,毕竟源码分析不是很在行呀,大家可以跟踪进去一探究竟吧。
友情提醒:若被
@LoadBalanced
注解的RestTemplate
访问正常的服务地址,如http://127.0.0.1:8080/hello
时,是会提示无法找到此服务的。具体原因:
serverid
必须是我们访问的服务名称
,当我们直接输入ip
的时候获取的server
是null
,就会抛出异常。此时,若是需要调用非注册中心的服务,可以创建一个不被
@LoadBalanced
注解的RestTemplate
,同时指定bean的名称,使用时,使用@Qualifier
指定name注入此RestTemplate
。负载均衡器
目前还未进行过自定义负载均衡,这里就简单的举例下,上次整理ppt时有讲过一些,但未深入了解过⊙﹏⊙‖∣,
可以从继承关系看出,是通过继承
IRule
来实现的。可继承ClientConfigEnabledRoundRobinRule,来实现自己负载均衡策略。
声明式服务Feign实例
从上一章节,我们知道,当我们要调用一个服务时,需要知道服务名和api地址,这样才能进行服务调用,服务少时,这样写觉得没有什么问题,但当服务一多,接口参数很多时,上面的写法就显得不够优雅了。所以,接下来,来说说一种更好更优雅的调用服务的方式:Feign。
Feign是Netflix开发的声明式、模块化的HTTP客户端。Feign可帮助我们更好更快的便捷、优雅地调用HTTP API。
在
Spring Cloud
中,使用Feign
非常简单——创建一个接口,并在接口上添加一些注解。Feign
支持多种注释,例如Feign自带的注解或者JAX-RS注解等
Spring Cloud对Feign进行了增强,使Feign支持了Spring MVC注解,并整合了Ribbon和 Eureka,从而让Feign 的使用更加方便。只需要通过创建接口并用注解来配置它既可完成对Web服务接口的绑定。Feign实例
创建个
spring-cloud-eureka-consumer-ribbon
工程项目。0.加入
feigin
依赖1.配置文件
2.创建启动类,加入注解
@EnableFeignClients
,开启feign
支持。3.创建一个接口类
IHelloClient
,加入注解@FeignClient
来指定这个接口所要调用的服务名称。4.创建一个demo控制层,引入此接口类。
是不是很简单,和调用本地服务是一样的了!
Feign继承特性
Feign支持继承,但不支持多继承。使用继承,可将一些公共操作分组到一些父类接口中,从而简化Feign的开发。
所以在实际开发中,调用服务接口时,可直接按接口类和实现类进行编写,调用方引入接口依赖,继承一个本地接口,这样接口方法默认都是定义好的,也少了很多编码量。用起来就更爽了,就是有点依赖性,对方服务修改后需要同步更新下,但这个团队内部约定下问题不大的
这里简单实例下,创建一个
spring-cloud-eureka-client-api
工程。0.加入依赖,注意此依赖的作用范围:
1.编写一个接口类
IHellpApi
:修改
spring-cloud-eureka-client
工程0.引入api依赖
1.创建一个
HelloApiImpl
类,实现IHelloApi
:此时,
HelloApiImpl
是个控制层也是个接口实现类了。修改
spring-cloud-eureka-consumer-feign
工程。
0.引入api依赖1.同样创建一个接口,使其继承
IHelloApi
:小技巧:可以在
IHelloApi
定义一个服务名变量,如:SERVICE_NAME,这样让提供者进行变量的赋值,可以避免一些不必要的交流成本的,若有变化,服务调用方也无需关心的。一切都是约定编程!2.修改下
DemoController
类,注入HelloApi
:3.分别启动各服务,访问:http://127.0.0.1:8028/hello2?name=oKong-api
使用起来没啥差别的,一样的调用,但对于调用方而言,可以无需去理会具体细节了,照着接口方法去传参就好了。
这种方式,和原来的
dubbo
调用的方式是类似的,简单方便。大家可以把接口和实体放入一个包中,调用者和提供者都进行依赖即可。注意事项
在使用
Feign
时,会碰见一些问题,为了避免不必要的错误,以下这些需要额外注意下。- GET请求多个参数时,需要使用@RequestParam
- GET请求参数为实体时,会自动转换成POST请求
- POST请求使用@RequestBody注解参数
- 不建议直接将@RequestMapping注解在类上,直接写在方法上
参考资料
总结
本章节主要讲解了下服务消费者如何利用原生、ribbon、fegin三种方式进行服务调用的,其实每种调用方式都是使用ribbon来进行调用的,只是有些进行了增强,是的使用起来更简单高效而已。对于其原理的实现,本文未进行详细阐述,大家可以谷歌想相关知识,跟踪下源码了解下,本人也尚未深入研究过,还是停留在使用阶段,之后有时间了看一看,有啥心得再来分享吧。此时若服务上线下线,调用者调用可能会出现短暂的调用异常,最常见的就是找不到服务,此时服务容错保护就排上用场了,所以下一章节,就来说说关于服务容错保护相关知识点~
@LoadBalanced注解与RestTemplate
在
Spring Cloud
微服务应用体系中,远程调用都应负载均衡。我们在使用RestTemplate
作为远程调用客户端的时候,开启负载均衡极其简单:一个@LoadBalanced
注解就搞定了。
相信大家大都使用过Ribbon
做Client端的负载均衡,也许你有和我一样的感受:Ribbon虽强大但不是特别的好用。我研究了一番,其实根源还是我们对它内部的原理不够了解,导致对一些现象无法给出合理解释,同时也影响了我们对它的定制和扩展。本文就针对此做出梳理,希望大家通过本文也能够对Ribbon
有一个较为清晰的理解(本文只解释它@LoadBalanced
这一小块内容)。开启客户端负载均衡只需要一个注解即可,形如这样:
说
Spring
是Java界最优秀、最杰出的重复发明轮子作品一点都不为过。本文就代领你一探究竟,为何开启RestTemplate
的负载均衡如此简单。说明:本文建立在你已经熟练使用RestTemplate,并且了解RestTemplate它相关组件的原理的基础上分析。若对这部分还比较模糊,强行推荐你先参看我前面这篇文章:RestTemplate的使用和原理你都烂熟于胸了吗?【享学Spring MVC】
RibbonAutoConfiguration
这是
Spring Boot/Cloud
启动Ribbon
的入口自动配置类,需要先有个大概的了解:这个配置类最重要的是完成了
Ribbon
相关组件的自动配置,有了LoadBalancerClient
才能做负载均衡(这里使用的是它的唯一实现类RibbonLoadBalancerClient
)@LoadBalanced
注解本身及其简单(一个属性都木有):
它最大的特点:头上标注有
@Qualifier
注解,这是它生效的最重要因素之一,本文后半啦我花了大篇幅介绍它的生效时机。
关于@LoadBalanced
自动生效的配置,我们需要来到这个自动配置类:LoadBalancerAutoConfiguration
LoadBalancerAutoConfiguration
这段配置代码稍微有点长,我把流程总结为如下几步:
LoadBalancerAutoConfiguration
要想生效类路径必须有RestTemplate
,以及Spring容器内必须有LoadBalancerClient
的实现Bean \1.LoadBalancerClient
的唯一实现类是:org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient
LoadBalancerInterceptor
是个ClientHttpRequestInterceptor
客户端请求拦截器。它的作用是在客户端发起请求之前拦截,进而实现客户端的负载均衡
restTemplateCustomizer()
返回的匿名定制器RestTemplateCustomizer
它用来给所有的RestTemplate
加上负载均衡拦截器(需要注意它的@ConditionalOnMissingBean
注解~)
不难发现,负载均衡实现的核心就是一个拦截器,就是这个拦截器让一个普通的
RestTemplate
逆袭成为了一个具有负载均衡功能的请求器LoadBalancerInterceptor
该类唯一被使用的地方就是
LoadBalancerAutoConfiguration
里配置上去~此拦截器拦截请求后把它的
serviceName
委托给了LoadBalancerClient
去执行,根据ServiceName
可能对应N多个实际的Server
,因此就可以从众多的Server中运用均衡算法,挑选出一个最为合适的Server
做最终的请求(它持有真正的请求执行器ClientHttpRequestExecution
)。LoadBalancerClient
请求被拦截后,最终都是委托给了
LoadBalancerClient
处理。它只有一个实现类
RibbonLoadBalancerClient
(ServiceInstanceChooser
是有多个实现类的~)。RibbonLoadBalancerClient
首先我们应当关注它的
choose()
方法:choose方法
:传入serviceId,然后通过SpringClientFactory
获取负载均衡器com.netflix.loadbalancer.ILoadBalancer
,最终委托给它的chooseServer()
方法选取到一个com.netflix.loadbalancer.Server
实例,也就是说真正完成Server
选取的是ILoadBalancer
。ILoadBalancer以及它相关的类是一个较为庞大的体系,本文不做更多的展开,而是只聚焦在我们的流程上
LoadBalancerInterceptor
执行的时候是直接委托执行的loadBalancer.execute()
这个方法:returnVal
是一个ClientHttpResponse
,最后交给handleResponse()
方法来处理异常情况(若存在的话),若无异常就交给提取器提值:responseExtractor.extractData(response)
,这样整个请求就算全部完成了。使用细节
针对
@LoadBalanced
下的RestTemplate
的使用,我总结如下细节供以参考:- 传入的
String
类型的url必须是绝对路径(http://...
),否则抛出异常:java.lang.IllegalArgumentException: URI is not absolute
serviceId
不区分大小写(http://user/...效果同http://USER/...
)
serviceId
后请不要跟port端口号了~~~
最后,需要特别指出的是:标注有
@LoadBalanced
的RestTemplate
只能书写serviceId
而不能再写IP地址/域名
去发送请求了。若你的项目中两种case都有需要,请定义多个RestTemplate
分别应对不同的使用场景~本地测试
了解了它的执行流程后,若需要本地测试(不依赖于注册中心),可以这么来做:
这么一来,下面这个访问结果就是百度首页的html内容喽。
此处my-serviceId肯定是不存在的,但得益于我上面自定义配置的LoadBalancerClient
什么,写死
return
一个Server
实例不优雅?确实,总不能每次上线前还把这部分代码给注释掉吧,若有多个实例呢?还得自己写负载均衡算法吗?很显然Spring Cloud
早早就为我们考虑到了这一点:脱离Eureka使用配置listOfServers进行客户端负载均衡调度(<clientName>.<nameSpace>.listOfServers=<comma delimited hostname:port strings>
)对于上例我只需要在主配置文件里这么配置一下:
效果完全同上。
Tips:这种配置法不需要是完整的绝对路径,http://是可以省略的(new Server()方式亦可)
自己添加一个记录请求日志的拦截器可行吗?
显然是可行的,我给出示例如下:
这样每次客户端的请求都会打印这句话:
当前请求的URI是:<http://my-serviceId
>,一般情况(缺省情况)自定义的拦截器都会在负载均衡拦截器前面执行(因为它要执行最终的请求)。若你有必要定义多个拦截器且要控制顺序,可通过Ordered
系列接口来实现~最后的最后,我抛出一个非常非常重要的问题:
@Autowired
+ @LoadBalanced
能把你配置的RestTemplate
自动注入进来拿来定制呢???核心原理是什么?> 提示:本原理内容属于Spring Framwork
核心技术,建议深入思考而不囫囵吞枣。有疑问的可以给我留言,我也将会在下篇文章给出详细解答(建议先思考)
推荐阅读
\------------------------------------------------------
Ribbon是如何通过一个@LoadBalanced注解就实现负载均衡的
原创绅士jiejie 最后发布于2019-11-08 15:09:04 阅读数 94 收藏
发布于2019-11-06 16:14:45
分类专栏: Spring Cloud
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
展开
一.介绍下测试用到的服务
从Eureka注册中心中可以可以看出有EUREKA-CLIENT和RIBBON-CLIENT的服务,其中EUREKA-CLIENT有两个节点作为服务提供者,而RIBBON-CLIENT则是服务消费者,通过RestTemplate来消费EUREKA-CLIENT的服务。
下面代码就是简单实现Ribbon负载均衡的配置类:
这样简单的通过一个@LoadBalanced注解在RestTemplate上 ,在RestTemplate 远程调用的时候,就会出现负载均衡的效果。
二.一步一步理清Ribbon负载均衡的逻辑
- 首先全局搜索@LoadBalanced这个注解,发现在LoadBalancerAutoConfiguration类有用到该注解:
分析以上代码:
- 通过@Configuration表明这是一个配置类
- 通过@ConditionalOnClass(RestTemplate.class)可以知道RestTemplate类要在类路径上存在才会实例化LoadBalancerAutoConfiguration
- 通过@ConditionalOnBean(LoadBalancerClient.class)可以知道LoadBalancerClient类要存在才会实例化LoadBalancerAutoConfiguration
- @EnableConfigurationProperties(LoadBalancerRetryProperties.class)是用来使用@ConfigurationProperties注解的类LoadBalancerRetryProperties生效,贴上部分LoadBalancerRetryProperties类的代码,会更清晰:
- 所以重启下RIBBON-CLIENT服务,Debug继续看LoadBalancerAutoConfiguration 类的代码,发现在启动时会先进入LoadBalancerAutoConfiguration 的loadBalancerRequestFactory方法,实例化出LoadBalancerRequestFactory
接下去断点进入LoadBalancerAutoConfiguration 类中的静态内部类LoadBalancerInterceptorConfig的ribbonInterceptor方法,可以看出这是为了实例化出LoadBalancerInterceptor 拦截器
继续跟断点,进入了loadBalancedRestTemplateInitializerDeprecated方法,可以看出这个方法里主要的逻辑代码是customizer.customize(restTemplate)
继续Debug,断点进入LoadBalancerAutoConfiguration类中的静态内部类LoadBalancerInterceptorConfig:
通过 list.add(loadBalancerInterceptor)和restTemplate.setInterceptors(list)两段代码可以看出,这是要给restTemplate加上loadBalancerInterceptor拦截器。
那么接下来看看loadBalancerInterceptor拦截器里做了什么,通过页面发起一个http请求,断点进入到LoadBalancerInterceptor类的intercept方法,
截图看下信息:
可以看到该方法取得了request里的url和servicName,然后将这些参数交给loadBalancer.execute去执行方法。而loadBalancer是LoadBalancerClient类的实例。
看下LoadBalancerClient的类图,可以看到LoadBalancerClient继承了ServiceInstanceChooser,LoadBalancerClient的实现类是RibbonLoadBalancerClient
逻辑继续,断点进入了RibbonLoadBalancerClient的execute方法
跟着断点一步一步看方法:
- ILoadBalancer loadBalancer = getLoadBalancer(serviceId); 经过这个方法,得到loadBalancer,从截图里可以看到,loadBalancer里有个allServerList集合,里面有两个对象,端口号分别是8763和8762,这就是我们提供的服务节点。
- Server server = getServer(loadBalancer, hint) 从图里可以看出,通过这个getServer方法,会返回给我们一个当前可调用的服务节点,而至于怎么返回服务节点,会再写一篇分析,写完后会更新链接到该篇。
- 生成RibbonServer 作为参数传入execute方法
- 运行execute方法
接着跟进execute方法
可以看该方法里的关键执行方法是:
T returnVal = request.apply(serviceInstance);
接着看apply方法,发现它是LoadBalancerRequest接口的方法,该接口却没有具体的实现类:
思路回溯,是request对象调用的apply方法,而request其实是execute方法传进来的参数,追溯到源头,发现是LoadBalancerInterceptor类的intercept方法里this.requestFactory.createRequest(request, body, execution)生成了LoadBalancerRequest,然后作为参数传入,之后再调用了apply方法
跟进createRequest方法里:
可以从图中看到,经过一些操作后,生成的serviceRequest对象里的serviceId是eureka-client,也就是我们的服务节点名,而server是localhost:8763,这是具体的服务节点ip,之后作为参数调用org.springframework.http.client包下的InterceptingClientHttpRequest类中的execute方法断点进入该方法:
可以看出通过requestFactory.createRequest(request.getURI(), method)方法生成了ClientHttpRequest类的实例delegate,它的url就是我们最后真正要请求的,最后正常调用delegate.execute()方法取得返回ClientHttpResponse就好了。
而这里产生了一个疑问,url是怎么产生的?重新发起请求断点试下
发现关键在LoadBalancerRequestFactory类中的createRequest方法中的这句:
跟进ServiceRequestWrapper类中,发现它继承了HttpRequestWrapper 类,同时重写了getURI方法
断点打在getURI方法里:
可以看到该方法返回了我们最后需要的url。
最后,关于Ribbon是如何通过一个@LoadBalanced注解就实现负载均衡的分析就到这了,还是有很多疏漏的地方,但是大致的逻辑就是这样的了,还有一些更深层的比如如何根据策略选出当前提供服务的节点等,留待后续补充,来日方长~
- 作者:Honesty
- 链接:https://blog.hehouhui.cn/archives/20
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章