Spring Cloud LoadBalancer负载均衡策略中的RestTemplate为什么打上@LoadBalanced注解就能以服务名访问到服务

Spring Cloud LoadBalancer负载均衡策略中的RestTemplate为什么打上@LoadBalanced注解就能以服务名访问到服务


为什么要学这个?你不想面试的时候多装两个逼多拿两千块钱?Robbin负载均衡分析,请看我的Robbin负载均衡文章。

**.
SpringBoot启动时,我们知道会根据工厂机制对自动化配置类进行加载。那么对于@LoadBalanced而言,对应的加载类是LoadBalancerAutoConfiguration。我们进入到此类看看,注意看我的注释说明!

@Configuration
@ConditionalOnClass({RestTemplate.class})
@ConditionalOnBean({LoadBalancerClient.class})
@EnableConfigurationProperties({LoadBalancerRetryProperties.class})
public class LoadBalancerAutoConfiguration {
    @LoadBalanced
    @Autowired(
        required = false
    )
    //1.获取到上下文对象中所有被@LoadBalanced注解修饰的RestTemplate!
    private List<RestTemplate> restTemplates = Collections.emptyList();
    @Autowired(
        required = false
    )
    private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

    public LoadBalancerAutoConfiguration() {
    }

    @Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
    //2.List<RestTemplateCustomizer>是上下文对象中存在的RestTemplateCustomizerBean的集合
        return () -> {
            restTemplateCustomizers.ifAvailable((customizers) -> {
                Iterator var2 = this.restTemplates.iterator();

                while(var2.hasNext()) {
                    RestTemplate restTemplate = (RestTemplate)var2.next();
                    Iterator var4 = customizers.iterator();

                    while(var4.hasNext()) {
                        RestTemplateCustomizer customizer = (RestTemplateCustomizer)var4.next();
                       	//3.遍历步骤1中获取的RestTemplate集合,并使用RestTemplateCustomizer集合给每个RestTemplate定制。
                        customizer.customize(restTemplate);
                    }
                }

            });
        };
    }

那这个RestTemplateCustomizer在定制的时候做了什么操作呢? 好,继续看,此类中的LoadBalancerInterceptorConfig配置类定义了RestTemplateCustomizer,下面是源码

@Configuration
//1.条件注解,LoadBalancerInterceptorConfig 只有在ClassLoader不存在RetryTemolate时才会生效(我们不管它)
@ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
    static class LoadBalancerInterceptorConfig {
        LoadBalancerInterceptorConfig() {
        }

        @Bean
        //2.定义LoadBalancerInterceptor Bean 这个拦截器继承ClientHttpRequestInterceptor,可以被添加到RestTemplate的拦截器列表中。
        public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }

        @Bean
        @ConditionalOnMissingBean
        //3.定义RestTemplateCustomizer  Bean 会在LoadBalancerAutoConfiguration 配置类中的restTemplateCustomizers存在
        public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
            return (restTemplate) -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor); //4.在RestTemplate的拦截器中添加LoadBalanceInterceptor拦截器!
                restTemplate.setInterceptors(list);
            };
        }
    }

好,到这里总结一下,用@LoadBalanced直接修饰的RestTemplate会被添加一个LoadBalanceInterceptor拦截器!!! 好!那么这个拦截器做了什么呢?咱们继续往下看。

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    private LoadBalancerClient loadBalancer;
    private LoadBalancerRequestFactory requestFactory;

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) { 
    // 1.构造器!构造器需要LoadBalancerClient 和LoadBalancerRequestFactory 参数(默认会在LoadBalancerAutoConfiguration里被构造,开发人员可以进行覆盖)。前者根据负载均衡请求(LoadBalancerRequest)和服务名做真正的服务调用;后者是个工厂,负责构造负载均衡请求LoadBalancerRequest,构造过程会使用LoadBalancerRequestTransformer对请求做一些自定义的转换。
        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();
        //2.服务名使用URI中的Host信息。
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
        //3.使用LoadBalancerClient客户端负载均衡器做真正的服务调用!!!!
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    }
}

总结,好,现在我们知道,被@LoadBalanced直接修饰的RestTemplate发出的请求,最后会被LoadBalanceInterceptor拦截!最后由LoadBalancerClient发出真正的请求。下面我们简单了解一下LoadBalancerClient。 LoadBalancerClient也叫客户端负载均衡器,会根据负载均衡请求和服务名执行真正的负载均衡操作。看看源码:

public interface LoadBalancerClient extends ServiceInstanceChooser {
	/**
	serviceId:服务名
	request:负载均衡请求
	返回值:基于选中的ServiceInstance实例在负载均衡请求下返回的结果
	**/
    <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

	/**
	serviceId:服务名
	serviceInstance服务实例
	request:负载均衡请求
	返回值:基于选中的ServiceInstance实例在负载均衡请求下返回的结果
	**/
    <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;

	/**
	使用ServiceInstance中的host和port构造出真正的URI。
	比如!http://我的服务名/path/to/service这个URI里的“我的服务名”对应的hsot是127.0.0.1,port是8083,最后都会被构造成真正的URI!!!!
	此方法的参数:
		instance,服务实例
		original,老URI
		返回值:构造后的URI
	**/
    URI reconstructURI(ServiceInstance instance, URI original);
}

为什么有两个重载的方法呢,其中一个还没有ServiceInstance对象。(关于ServiceInstance对象是什么,里面有哪些熟悉,大家自己百度下,简单说就是咱们通过服务发现,把发现的服务通过ServiceInstance对象的形式,保存在内存中)。 如果调用没有ServiceInstance参数的方法,我们的execute方法内部就会通过choose方法使用负载均衡算法得到一个serviceInstance!!!然后再去调用有serviceInstance的execute方法!

好了 面试达到这儿其实就可以了。下面我来给大家总结一下!!!
@LoadBalanced注解修饰的RestTemplate,会被RestTemplateCustomizer进行包装,强行的为其增加一个拦截器,在使用RestTemplate+服务名发请求的时候,会被LoadBalanceIntercepto拦截器拦截。拦截之后会根据服务名去获取服务实例,也就是ServiceInstance!!! 这个ServiceInstance对象内封装了服务的所有属性,包括我们最关心的Host和Port!! 拦截器获取到服务实例之后会由LoadBalancerClient 对象进行正式的服务调用!LoadBalancerClient 内的三个接口就在上面我刚刚发出来你们应该没忘吧。好,这就是RestTemplate为什么打上@LoadBalanced注解就能以服务名访问到服务的底层原理!

关于一些其他的知识例如自定义负载均衡算法,@LoadBalancerClient注解等我在这里就赘述了。大家看完了点个赞,码字不容易啊。。。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

易柏州Innovation

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值