我们如何知道负载均衡每次请求了哪一个服务呢?
关于ribbon的资料网络上已经很多了我不多做介绍,想要了解的同学自行百度
我们知道ribbon整合restTemplate 只需要 添加一个@LoadBalanced 注解即可如下
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
其实ribbon 能够做负载均衡是通过拦截器做的,我们先看看
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration 这个类做了什么事
@Configuration
@ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
static class LoadBalancerInterceptorConfig {
LoadBalancerInterceptorConfig() {
}
@Bean
public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
return (restTemplate) -> {
List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
我们可以看到第二个RestTemplateCustomizer 中添加了一个loadBalancerInterceptor 拦截器,如果要看源码的同学可以从这里开始看
LoadBalancerInterceptor.java
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
private LoadBalancerClient loadBalancer;
private LoadBalancerRequestFactory requestFactory;
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
this.loadBalancer = loadBalancer;
this.requestFactory = requestFactory;
}
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
}
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}
}
实际上就是在这里拿到原本的服务uri 比如 SERVICE-USER 然后将之替换成 192.168.0.1:8081 。这个ip +port 是从eureka 注册中心获取来的。这里不展开讲。那么知道了原理我们就来进入我们的正题。
我们同时也可以看到自定义的RestTemplateCustomizer 有个注解 @ConditionalOnMissingBean 如果spring容器不存在这个bean 则声明一个,有兴趣了解Conditional注解的同学可以自行拓展
也就是说我们如果需要知道每次ribbon请求了哪个服务。那么我们同样可以写个拦截器,拦截restTemplate 请求拿到uri并打印出来,那么我们肯定不能在他还没拿到真正请求之前去拦截他,意味着我们需要在他被 ribbon的拦截器拦截之后我们再次拦截就可以取到真正的地址了
自定义拦截器 CustomRestTemplateInterceptor.java
public class CustomRestTemplateInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
System.out.println("本次请求的服务器是:"+ request.getURI());
return execution.execute(request, body);
}
}
restTemplate配置
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
@Bean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
list.add(new CustomRestTemplateInterceptor());
restTemplate.setInterceptors(list);
};
}
}
请求
@GetMapping("/get_user")
public Result<User> getUser(){
Result<User> result = restTemplate.getForObject("http://SERVICE-USER/user/get_user", Result.class);
return result;
}
点两次之后查看日志你会发现我们获取到了请求的真正地址是什么入下图