Ribbon(3)--->spring-cloud生态中对负载均衡loadbalancer标准规范

1、我们知道spring-cloud是一个生态,它将微服务时代的很多组件进行了统一规范,并定义接口,这样就能够跟具体的实现进行解耦,这就是软件设计的开闭原则,我们知道在spring-cloud中,对于服务间的远程通讯都是使用rest规范来进行远程调用的,所以我们之前对RestTemplate进行详细的讲解。

2、spring-cloud中对很多微服务架构中的组件都进行了标准规范以及接口的定义,这些定义都在spring-cloud-commons包中,如下:

       

 

3、我们今天讲解的负载均衡也在其中,那么我们来看看spring-cloud生态中,对负载均衡loadbalancer做了什么规范以及定义了什么接口,先看loadbalancer包下面有什么内容:

       

       我们可以看出,包含了如下核心接口以及类:

             1、负载均衡器的服务选择器接口 ServiceInstanceChooser:作用就是根据服务ID serviceId 获取一个服务实例ServiceInstance,而ServiceInstance里面封装了ip、port、是否是https协议、服务的地址等信息。

             2、负载均衡客户端接口 LoadBalancerClient:作用就是根据被选中的服务实例ServiceInstance来进行请求URL的重构 以及 执行 LoadBalancerRequest 请求,LoadBalancerClient extends 了服务选择接口ServiceInstanceChooser。

                                                                                        LoadBalancerClient的执行方法有两个:

                                                                                          Ⅰ、execute(String serviceId, LoadBalancerRequest<T> request):只是通过serviceId + LoadBalancerRequest 来执行请求,那么在实现LoadBalancerClient的                                                                                                      时候,对此方式进行覆盖的时候就需要进行服务选择。

                                                                                          Ⅱ、execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request):通过serviceId + serviceInstance + LoadBalancerRequest                                                                                                     来执行请求,那么在覆盖此方法的时候就无需进行服务选择了。

             3、负载均衡请求接口 LoadBalancerRequest:本身就是一个请求实例,里面只有一个apply方法,入参是服务实例ServiceInstance。

             4、负载均衡请求工厂实现类 LoadBalancerRequestFactory:是一个实现类,专门用来构建 LoadBalancerRequest 实例的,其中的createRequest(...)方法会返回一个类型是LoadBalancerRequest的实例,且实现方式是返回一个lamda表达式。

             5、负载均衡拦截器实现类 LoadBalancerInterceptor:没错,它可以成为RestTemplate中的拦截器,在这个拦截器中进行服务选择、服务调用,这个是核心中的核心!!!!!

             6、负载均衡自动配置类 LoadBalancerAutoConfiguration:负责将负载均衡相关的Be'an,比如 负载均衡客户端LoadBalancerClient、RestTemplates、LoadBalancerRequestFactory、LoadBalancerInterceptor等Bean自动配置到spring的上下文中。

 

              那么这些类的关系是啥呢?如下图:

                                             

                                   从上如中我们可以看出,我们要想按照这个标准来实现一个负载均衡其实很简单了,spring-cloud把能实现的都实现了,你只需要实现一个负载均衡客户端LoadBalancerClient即可啊,看看spring多贴心。

 

4、上面我们并没有说负载均衡自动配置类 LoadBalancerAutoConfiguration的作用,接下来我们来讲解一下:

      我们在上面描述了 LoadBalancerAutoConfiguration的作用是负责将负载均衡相关的Be'an,比如 负载均衡客户端LoadBalancerClient、RestTemplates、LoadBalancerRequestFactory、LoadBalancerInterceptor等Bean自动配置到spring的上下文中,那么它是怎么样进行自动装配的呢?LoadBalancerAutoConfiguration源码如下:

@Configuration(proxyBeanMethods = false)

当前类路径先必须存在RestTemplate类
@ConditionalOnClass(RestTemplate.class)

当前的spring上下文中必须有LoadBalancerClient的实现类的BeanDefinition
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

    核心!!!依赖注入RestTemplate类型Bean,但是只注入被@LoadBalanced注解标记的RestTemplate,其实@LoadBalanced = @Qualifier , 有标记效果。
	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();

    依赖注入负载均衡请求的转换者,在使用LoadBalancerRequestFactory创建LoadBalancerRequest后使用其进行转换处理。
	@Autowired(required = false)
	private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

    SmartInitializingSingleton 在shring context的生命周期中的执行时机是所有的单例bean 实例化+初始化完成后执行SmartInitializingSingleton的void afterSingletonsInstantiated()
	@Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
			final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
			for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
				for (RestTemplateCustomizer customizer : customizers) {
                    使用已有的RestTemplateCustomizer Bean 来自定义每一个restTemplate Bean
					customizer.customize(restTemplate);
				}
			}
		});
	}

    装配一个负载均衡请求工厂实例LoadBalancerRequestFactory,很奇怪LoadBalancerRequestFactory 为什么会需要一个负载均衡客户端??? 其实很简单,在LoadBalancerRequestFactory在创建LoadBalancerRequest的时候,实现是返回一个lamda,这个lamda里的实现就是创建一个ServiceRequestWrapper实例,然后执行这个ServiceRequestWrapper实例,这个ServiceRequestWrapper实例就是对HttpRequest的包装,且ServiceRequestWrapper 覆盖了HttpRequest 的 getURI(),实现的逻辑是重构请求的url。
	@Bean
	@ConditionalOnMissingBean
	public LoadBalancerRequestFactory loadBalancerRequestFactory(
			LoadBalancerClient loadBalancerClient) {
		return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
	}

	@Configuration(proxyBeanMethods = false)
    不存在RetryTemplate的时候激活
	@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
	static class LoadBalancerInterceptorConfig {

        自动装配LoadBalancerInterceptor Bean
		@Bean
		public LoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}

        自动装配一个RestTemplateCustomizer 可理解为RestTemplate的初始化器
		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
                看见没,将restTemplate 添加负载均衡拦截器。
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}

	}

    。。。
}

装配负载均衡套件的过程其实很简单了。

 

5、执行流程总结:

        

 

               我们来看看InterceptingRequestExecution的execute(HttpRequest request, byte[] body) 方法的实现:也就是图中LoadBalancerRequestFactory 返回的表示 LoadBalancerRequest实例的那一段lamda的最后一行代码execution.execute(serviceRequest, body); 此处代码触发InterceptingRequestExecution的execute(HttpRequest request, byte[] body) 其方法的源码如下:

		@Override
		public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {

            迭代执行拦截器
			if (this.iterator.hasNext()) {
				ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
				return nextInterceptor.intercept(request, body, this);
			}

            如果拦截器链执行完成,开始执行请求。
			else {
				HttpMethod method = request.getMethod();
				Assert.state(method != null, "No standard HTTP method");

                此时的入参request 是 ServiceRequestWrapper实例,那么由于
ServiceRequestWrapper复写了HttpRequest的getURI()方法,实现的逻辑是调用负载均衡客户端
LoadBalancerClient根据服务实例ServiceInstance来重构url

				ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
				request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));
				if (body.length > 0) {
					if (delegate instanceof StreamingHttpOutputMessage) {
						StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
						streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(body, outputStream));
					}
					else {
						StreamUtils.copy(body, delegate.getBody());
					}
				}

                URL已经重构完成 , 那就按照RestTemplate的规范执行Http请求。
				return delegate.execute();
			}
		}

         有了以上的讲解,我相信实现一个自己的负载均衡客户端应该是一件很简单的事情了吧。接下来我们将去看看spring-cloud-netflix-ribbon 是如何实现负载均衡客户端的。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值