关于Spring Cloud LoadBalancer的简单记录(Nacos环境下)

  近日使用研究SpringCloud时,关注了一下Nacos的服务注册和发现。Nacos作为有个非常优秀的服务注册于发现平台,功能是十分强大的。服务发现和注册是微服务架构下最基本的功能也是最核心的功能,是微服务思想得以实现的重要平台。在微服务中有一个最基本的概念就是负载均衡,针对不同的服务器性能,服务的执行效率往往会受到或大或小的影响,那么如何去实现能者多劳呢,这时候就需要实现负载均衡算法,在SpringCloud LoadBalancer为我们构建了负载均衡的框架,并且提供了简单的负载均衡算法(RandomLoadBalancerRoundRobinLoadBalancer),在引入Nacos后Nacos会向SpringCloud LoadBalancer中注入自定义的负载均衡器,用户只需要在配置类中注入 RestTemplate 的Bean并且加上注解 @LoadBalancer 便可实现负载均衡,接下来逐步分析该实现过程。
  先分析一下在 LoadBalancer 下使用 RestTemplate发起HTTP请求的程序调用流程图:
在这里插入图片描述
  由图中可以看出,实现负载均衡的最基本的一点就是向RestTemplate中添加了一个拦截器(此处的拦截器和SpringMVC中的拦截器其实差不多),对请求的参数进行拦截并处理,在请求参数处理完成后利用处理的结果进行HTTP请求。实现负载均衡的逻辑就在其中。
进入拦截器中,会从请求URI上拿到服务名,并调用 requestFactory.createRequest() 生成 LoadBalancerRequest 可以理解为一个请求数据包(此处无关紧要)

    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));
    }

继续跟进,进入LoadBalancerClient类,其中最重要的一个点就是choose()这个方法,在这个方法中根据会利用 LoadBalancerClientFactory(应该理解为LoadBalancerFactory,因为LoadBalancerClient中调用了它,实际上它是LoadBalancer的工厂),它是ReactiveLoadBalancer.Factory得实现, 根据serviceId 在Map中查找应用上下文信息AnnotationConfigApplicationContext,利用上下文的getBean()获取到负载均衡器。在引入Nacos后我们获取到的LoadBalancer是NacosLoadBalancer这就是Nacos的自定义负载均衡器。NacosLoadBalancer实现自ReactorServiceInstanceLoadBalancerRandomLoadBalancerRoundRobinLoadBalancer也实现了它,最后利用NacosLoadBalancer查找到Serviceinstance,从而实现负载均衡。

	@Override
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
		String hint = getHint(serviceId);
		LoadBalancerRequestAdapter<T, TimedRequestContext> lbRequest = new LoadBalancerRequestAdapter<>(request,
				buildRequestContext(request, hint));
		Set<LoadBalancerLifecycle> supportedLifecycleProcessors = getSupportedLifecycleProcessors(serviceId);
		supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
		ServiceInstance serviceInstance = choose(serviceId, lbRequest);
		if (serviceInstance == null) {
			supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onComplete(
					new CompletionContext<>(CompletionContext.Status.DISCARD, lbRequest, new EmptyResponse())));
			throw new IllegalStateException("No instances available for " + serviceId);
		}
		return execute(serviceId, serviceInstance, lbRequest);
	}
	@Override
	public <T> ServiceInstance choose(String serviceId, Request<T> request) {
		ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId);
		if (loadBalancer == null) {
			return null;
		}
		Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block();
		if (loadBalancerResponse == null) {
			return null;
		}
		return loadBalancerResponse.getServer();
	}

  到这里RestTemplate得执行流程就分析完成。接下来需研究拦截器LoadBalancerInterceptor和自定义ReactorServiceInstanceLoadBalancer实现类如何注入到我们在配置类中向容器中注入的RestTemplate中。
在spring-cloud-commons:3.1.5这个jar包内提供了一系列的SpringCloud相关的标准还有一些自动配置类,进入spring.factories内找到自动配置类LoadBalancerAutoConfiguration其中提供了LoadBalancer相关的自动配置向在这里SpringCloud注入了LoadBalancerInterceptor的Bean,与此同时注入了一个RestTemplateCustomizer的Bean,RestTemplateCustomizer是一个方法接口,RestTemplateCustomizer的作用是将前面的LoadBalancerInterceptor设置到restTemplate中,到这里拦截器的设置流程就结束了,在最后SpringCloud还利用扩展点SmartInitializingSingleton来完成流程的执行。

@Configuration(proxyBeanMethods = false)
	@Conditional(RetryMissingOrDisabledCondition.class)
	static class LoadBalancerInterceptorConfig {

		@Bean
		public LoadBalancerInterceptor loadBalancerInterceptor(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);
			};
		}

	}
	@Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
			final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
			for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
				for (RestTemplateCustomizer customizer : customizers) {
					customizer.customize(restTemplate);
				}
			}
		});
	}

查看SmartInitializingSingleton描述,可知该接口类似于Spring上下文准备完成后的事件,在上下文准备完成后调用该扩展点。

此回调变体有点类似于org.springframework.context.event.ContextRefreshedEvent,但不需要实现 org.springframework.context.ApplicationListener,无需跨上下文层次结构等过滤上下文引用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值