1.背景
在研究玩自动装配后,我搭了个demo,总共三个eureka server构成集群,然后两个服务提供者provider,一个consumer。在上一章简单了解自动装配的内容后,现在看看restTemplate是如何一步步走到ribbon的负载均衡器的。
// 这里是直接处理了默认的restTemplate
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
@GetMapping("/hello2")
public String sayHelloWithProvider2(){
String url = "http://ServiceProvider/provider/hello";
return restTemplate.getForObject(url, String.class);
}
2.源码调试观测到LoadBalancerInterceptor
上面找到了拦截器,下面就是进入拦截器了
3.LoadBalanceInterceptor到底在干啥?
在LoadBalanceInterceptor中,
首先是获得了服务名ServiceProvider,
然后通过LoadBalanceRequestFactory的createRequest将HttpRequest,body,ClientHttpRequestExecution封装成LoadBalanceRequest。
最后进入了RibbonLoadBalancerClient进行ribbon相关的执行。
所以LoadBalancerInterceptor就是将请求进行封装,然后转换成LoadBalancerClient需要的类型。
4.RibbonLoadBalancerClient在干啥?
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
// 选择负载均衡器,默认是DynamicServerLsitLoadBalancer
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
// 通过loadBalancer选择服务
Server server = getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
// 将获得的服务实例信息封装成RibbonServer
RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
serviceId), serverIntrospector(serviceId).getMetadata(server));
// 拿着ribbonServer去处理请求
return execute(serviceId, ribbonServer, request);
}
在这里总结一下:
1.获取负载均衡器(getLoadBalancer
)
2.通过负载均衡器从服务列表中选择一个服务,这个服务将作为被发送的服务器。(getServer
)
- 执行请求
execute(serviceId, ribbonServer, request)
其实我们可以想象到,负载均衡器最关键的地方其实就是如何拿去到服务列表,然后如何从服务列表中选择服务。
所以后面会单独出一篇来讲,如何进行服务列表获取和服务选择。
5.RibbonLoadBalancerClient的execute又干了啥?
1.基于servcieId开启ribbon统计
2.request.apply LoadBalanceRequest的匿名方法处理
org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory#createRequest
public LoadBalancerRequest<ClientHttpResponse> createRequest(final HttpRequest request,
final byte[] body, final ClientHttpRequestExecution execution) {
// 创建的LoadBalancerRequest自带一个方法apply,这里是匿名实现
// 这里其实也在做如何将LoadBalancerRequest再封装成为HttpRequest,毕竟最后还是发http请求出去
return instance -> {
// 这里使用了一个ServiceRequestWrapper将服务实例信息和负载均衡器封装起来
HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer);
if (transformers != null) {
// 这里可以通过自定义ServiceRequestWrapper来改变HttpRequest信息
for (LoadBalancerRequestTransformer transformer : transformers) {
serviceRequest = transformer.transformRequest(serviceRequest, instance);
}
}
// 这里就又走到了spring web 的http请求发送类去了,在这个方法中
// 完成了 ServiceProvider到192.168.1.80:9955的服务名替换,后面就是正常的http发送流程了。
return execution.execute(serviceRequest, body);
};
}
6.execution.execute在做啥?
其实它就是用来真正处理http请求的。这个包是spring-web包下的,其实是不存在跟ribbon相关的内容的。
Ribbon是如何将ServiceProvider这个服务名替换成IP+端口呢?
request.getURI()就是Ribbon完成转完的关键,Ribbon是通过继承HttpRequest,然后重写了getURI
没错,这就完成了服务名到ip和端口的替换。
7.小结
ribbon作为一个负载均衡工具,它拦截了requestTemplate,并做了一系列的操作来实现了通过服务名负载均衡访问其他服务。上面是整个拦截流程的大致过程,这里再总结一下。
- restTemplate按照自己执行流程进入InterceptingClientHttpRequest中,在这里发现了LoadBalancerInterceptor,并进入LoadBalancerInterceptor中的intercept方法
- 在intercept()方法中执行了LoadBalancerClient的execute方法,这个方法是负载均衡的核心
- execute方法中首先会获取负载均衡器ILoadBalancer,这个决定了到底从哪获取服务实例列表
- 然后通过getServer(loadBalancer,hint)方法获取服务实例列表并根据指定的Rule去计算出使用哪个服务实例
- 将服务实例包装到HttpRequest中(ServiceRequestWrapper就是HttpRequest和服务实例的包装)
- 通过重写getURI方法达到通过服务实例信息替换服务实例名到ip+端口的过程。
以上就是ribbon整个基于restTemplate运行的过程。这里我们看到有几个很重要的类或者接口:ILoadBalancer,LoadBalancerClient,LoadBalancerRequestFactory,ServiceRequestWrapper,IRule
这几个基本上应该算是整个ribbon的核心了。
后面几篇将针对这几个来进行详细解析。