“不积跬步,无以至千里。”
接着上一篇文章,
return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
显然微服务调用的逻辑都在这个LoadBalancerClient
组件的execute方法中,那么这个组件是从哪里来的
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
可以发现,这个负责负载均衡的组件是在定义Bean的时候直接在形参中声明的,所以一定在一个地方注册到了spring的工厂
老规律,肯定是一个自动配置类
我直接告诉你得了,你去找我估计你要找到天荒地老
是一个叫RibbonAutoConfiguration
的配置类中注册spring容器的
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
而且这个配置类还加了一个限定,@AutoConfigureBefore
,会在之前看的那个LoadBalancerAutoConfiguration
之前来执行,因为两个有依赖关系,依赖的就是这个负载均衡组件RibbonLoadBalancerClient
,明白了吧
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
也就是说,所有的微服务调用都会由RibbonLoadBalancerClient#execute方法来进行处理
现在来重点看看这个核心方法
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
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);
}
把断点打在这个execute方法,使用浏览器发起请求,断点直接就进来了,可以看到此时已经获取到了服务Id,也就是服务名称
getLoadBalancer(serviceId)
,这行代码是说,根据这个服务Id获取一个负载均衡组件ILoadBalancer
,是一个DynamicServerListLoadBalancer
,这一块是核心,会在下篇文章将获取这个组件的全流程进行刨析,这里先有个印象
可以大致看一下这个负载均衡组件里面的信息
DynamicServerListLoadBalancer:{NFLoadBalancer:name=springboot-project-2,current list of Servers=[localhost:10001],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:1; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;]
},Server stats: [[Server:localhost:10001; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@682278
current list of Servers=[localhost:10001]
,其实包含了被调服务的服务注册表,那么下面肯定有一个chooseServer之类的方法,从服务列表中,基于负载均衡算法,挑选出来一个server,发起http请求
Server server = getServer(loadBalancer)
当然这个方法就是,传进去刚刚获取到的负载均衡组件,然后一定是基于其内部的一个Rule来从Server List中挑选一个Server,猜测
然后调用重载的execute方法,发起实际的http请求
public LoadBalancerRequest<ClientHttpResponse> createRequest(final HttpRequest request,
final byte[] body, final ClientHttpRequestExecution execution) {
return instance -> {
HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer);
if (transformers != null) {
for (LoadBalancerRequestTransformer transformer : transformers) {
serviceRequest = transformer.transformRequest(serviceRequest, instance);
}
}
return execution.execute(serviceRequest, body);
};
}
这个ClientHttpRequestExecution
,实际就是调用spring原生的httpclient,发送http请求了
这里可以看到,在最终执行http请求的时候,服务名称已经替换成了ip:port
的格式了,而且如果我们的被调服务如果部署在多台机器上面,那么这个ip会基于负载均衡组件内置的IRule
组件来进行负载均衡的调用,最终拿到结果
ok,总结一下
其实Ribbon主要的工作就是,
- 根据服务Id,也就是服务名称(被调服务),先拿到一个核心组件
ILoadBalancer
,这里面包含被调服务的Server List,服务列表 - 基于
ILoadBalancer
内置的IRule
组件,负载均衡算法,从服务列表中挑选一个Server - 基于Spring原生的http组件
ClientHttpRequestExecution
,发起http请求,最终拿到响应并返回,over!
后面的文章会重点分析一下,这个ILoadBalancer
的初始化过程,以及如何里面的服务列表从哪里来的?其实想想也知道,肯定是跟Eureka进行整合,然后把Eureka本地缓存的服务注册表拿来用的,然后Eureka定时要从Server端更新自己的本地注册表,那么Ribbon肯定也要有这么个东西,来定时拉取最新的注册表之类的。。。
还有比较重要的就是ribbon是怎么从一堆服务中挑选出来每次要执行http请求的真正server的?
最后,附上一张手绘的ribbon大体的运行流程图吧,ribbon这个组件大体来说还是比较简单的