近日使用研究SpringCloud时,关注了一下Nacos的服务注册和发现。Nacos作为有个非常优秀的服务注册于发现平台,功能是十分强大的。服务发现和注册是微服务架构下最基本的功能也是最核心的功能,是微服务思想得以实现的重要平台。在微服务中有一个最基本的概念就是负载均衡,针对不同的服务器性能,服务的执行效率往往会受到或大或小的影响,那么如何去实现能者多劳呢,这时候就需要实现负载均衡算法,在SpringCloud LoadBalancer为我们构建了负载均衡的框架,并且提供了简单的负载均衡算法(RandomLoadBalancer、RoundRobinLoadBalancer),在引入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实现自ReactorServiceInstanceLoadBalancer,RandomLoadBalancer、RoundRobinLoadBalancer也实现了它,最后利用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,无需跨上下文层次结构等过滤上下文引用。