在使用spring cloud搭建微服务架构时,需要进行负载均衡操作。负载均衡分为硬件负载均衡和软件负载均衡,软件负载均衡又分为服务端负载均衡和客户端负载均衡。本系列主要介绍利用Spring cloud Ribbon 和RestTemplate实现客户端负载均衡,本文主要介绍将逻辑名为host的URI转化为服务实例的过程。
在进行开发时,要实现对基于RestTemplate的客户端负载均衡,只需在创建RestTemplate对象时,添加LoadBalanced注解即可实现。通过查看源码,该注解是通过LoadBalanceClient接口实现其功能。LoadBalanceClient接口定义三个方法,源码如下:
1 public interface LoadBalancerClient { 2 ServiceInstance choose(String var1); 3 4 <T> T execute(String var1, LoadBalancerRequest<T> var2) throws IOException; 5 6 URI reconstructURI(ServiceInstance var1, URI var2); 7 }
这三个方法之间的关系:在自动配置完成相关配置后(下文介绍), execute()方法执行的第一步需要选择一个服务实例,及调用choose()方法(实质上并非使用这个chosse()方法,下文介绍)。选择完服务实例后,调用reconstructURi()方法重新组织成最终的URI,完成URI转换工作。
前面从总体上概要介绍了LoadBalanceClient的工作原理,其中提到的自动配置主要用LoadBalancedAutoConfiguration自动配置类完成,源码如下:
@Configuration @ConditionalOnClass({RestTemplate.class}) @ConditionalOnBean({LoadBalancerClient.class}) public class LoadBalancerAutoConfiguration { @LoadBalanced @Autowired( required = false ) private List<RestTemplate> restTemplates = Collections.emptyList(); public LoadBalancerAutoConfiguration() { } @Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializer(final List<RestTemplateCustomizer> customizers) { return new SmartInitializingSingleton() { public void afterSingletonsInstantiated() { Iterator var1 = LoadBalancerAutoConfiguration.this.restTemplates.iterator(); while(var1.hasNext()) { RestTemplate restTemplate = (RestTemplate)var1.next(); Iterator var3 = customizers.iterator(); while(var3.hasNext()) { RestTemplateCustomizer customizer = (RestTemplateCustomizer)var3.next(); customizer.customize(restTemplate); } } } }; } @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) { return new RestTemplateCustomizer() { public void customize(RestTemplate restTemplate) { List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } @Bean public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient) { return new LoadBalancerInterceptor(loadBalancerClient); } }
负载均衡自动配置类主要提供三个功能,辅助实在客户端负载均衡:
第一小节定义了一个实现客户端负载的接口,在Ribbon中其实现类是RibbonLoadBalanceClient,部分源码如下:
public class RibbonLoadBalancerClient implements LoadBalancerClient { private SpringClientFactory clientFactory; public RibbonLoadBalancerClient(SpringClientFactory clientFactory) { this.clientFactory = clientFactory; } public URI reconstructURI(ServiceInstance instance, URI original) { Assert.notNull(instance, "instance can not be null"); String serviceId = instance.getServiceId(); RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId); Server server = new Server(instance.getHost(), instance.getPort()); boolean secure = this.isSecure(server, serviceId); URI uri = original; if (secure) { uri = UriComponentsBuilder.fromUri(original).scheme("https").build().toUri(); } return context.reconstructURIWithServer(server, uri); } public ServiceInstance choose(String serviceId) { Server server = this.getServer(serviceId); return server == null ? null : new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server)); } public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException { ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId); Server server = this.getServer(loadBalancer); if (server == null) { throw new IllegalStateException("No instances available for " + serviceId); } else { RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server)); RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId); RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server); try { T returnVal = request.apply(ribbonServer); statsRecorder.recordStats(returnVal); return returnVal; } catch (IOException var9) { statsRecorder.recordStats(var9); throw var9; } catch (Exception var10) { statsRecorder.recordStats(var10); ReflectionUtils.rethrowRuntimeException(var10); return null; } } } }
...
protected Server getServer(ILoadBalancer loadBalancer) {
return loadBalancer == null ? null : loadBalancer.chooseServer("default");
}
}
在该类中,通过执行execute()方法下完成URL转换,主要步骤如下:
DynamicServerListLoadBalancer
的扩展,规避跨区域(Zone)访问,减少延迟)。Spring Clond Ribbon采用的就是ZoneAwareLoadBalancer(区域感知负载均衡器)作为IloadBalancer的实现类,以此对选择客户端服务实例。下一篇文章介绍多种负载均衡器。
深入理解Spring Cloud Ribbon客户端负载均衡原理(一 实现服务实例地址转换)
原文:https://www.cnblogs.com/guojuboke/p/10417642.html