restTemplate和robbin实现负载均衡 原理解读

我们知道在springCloud中为RestTemplate贴上@LoadBalanced的注解即实现的对该RestTemplate的uri的替换和负载均衡;具体实现是怎样呢

LoadBalancerAutoConfiguration注入

  • 1,在spring-cloud-netflix-ribbon-2.2.1.RELEASE.jar的spring.factories中
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration

当启动类贴上EnableAutoConfiguration注解后会将spring容器就会加载RibbonAutoConfiguration

  • 2,如图在加载RibbonAutoConfiguration时会先加载LoadBalancerAutoConfiguration

restTemplate和robbin实现负载均衡 原理解读_第1张图片

LoadBalancerAutoConfiguration加载的Bean

我们主要关注该类的三个地方

  • 1 RestTemplateCustomizer->Bean加载 实现RestTemplateCustomizer的customize方法;customize方法的操作即为在spring容器中找到loadBalancerInterceptor这个拦截器并将其设置到restTemplate中
		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}
  • 2,将带有LoadBalanced注解的RestTemplate注入到restTemplates中
	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();
  • 3,通过loadBalancedRestTemplateInitializerDeprecated方法将loadBalancerInterceptor拦截器设置到所有的贴有注解LoadBalanced的RestTemplate中
    这样在RestTemplate进行RPC调用时就会调用到LoadBalancerInterceptor里面的intercept方法
	@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);
				}
			}
		});
	}

LoadBalancerInterceptor的intercept 执行流程

  • intercept里面最后调用的this.loadBalancer.execute我们分为两块来讲解
    • 1> this.loadBalancer.execute方法我们来看RibbonLoadBalancerClient的实现
      • 1.1 我们实际上调用的是这个方法,这里我们通过负载均衡策略得到了Server服务实例,并将Server进行了包装
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);// 其中serviceId即为服务名
		Server server = getServer(loadBalancer, hint);// 这里就是通过负载均衡策略获取到指的的server
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}
		// 对Server服务实例进行包装对象之外,还存储了服务名、是否使用https标识以及一个Map类型的元数据集合
		RibbonServer ribbonServer = new RibbonServer(serviceId, server,
				isSecure(server, serviceId),
				serverIntrospector(serviceId).getMetadata(server));

		return execute(serviceId, ribbonServer, request);
	}
  • 1.2我们在看接下来的execute方法,会调用最开始this.loadBalancer.execute的request参数的实现类的apply方法,并存储调用情况
	@Override
	public <T> T execute(String serviceId, ServiceInstance serviceInstance,
			LoadBalancerRequest<T> request) throws IOException {
		Server server = null;
		if (serviceInstance instanceof RibbonServer) {
			server = ((RibbonServer) serviceInstance).getServer();
		}
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}
		// RibbonLoadBalancerContext用于存储调用情况
		RibbonLoadBalancerContext context = this.clientFactory
				.getLoadBalancerContext(serviceId);
		RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

		try {
		// 这里会调用最开始的this.loadBalancer.execute的request参数的实现类的apply方法
			T returnVal = request.apply(serviceInstance);
			// 存储调用情况
			statsRecorder.recordStats(returnVal);
			return returnVal;
		}
		// catch IOException and rethrow so RestTemplate behaves correctly
		catch (IOException ex) {
			statsRecorder.recordStats(ex);
			throw ex;
		}
		catch (Exception ex) {
			statsRecorder.recordStats(ex);
			ReflectionUtils.rethrowRuntimeException(ex);
		}
		return null;
	}
  • 2> this.loadBalancer.execute方法的第二个参数this.requestFactory.createRequest(request, body, execution)
public LoadBalancerRequest<ClientHttpResponse> createRequest(
			final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) {
		return instance -> {
			// 将request转为了serviceRequest 主要是将loadBalancer.reconstructURI()的实现来重写里面的getURI()方法
			HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance,
					this.loadBalancer);
			if (this.transformers != null) {
				for (LoadBalancerRequestTransformer transformer : this.transformers) {
					serviceRequest = transformer.transformRequest(serviceRequest,
							instance);
				}
			}
			return execution.execute(serviceRequest, body);
		};
	}

该方法中的lambda表达式及上文中1.2中的apply的方法实现;这里主要关注HttpRequest serviceRequest转换,将获取uri的方式由原来的getURI()改为loadBalancer.reconstructURI()方法的实现

这样最终即为贴有@LoadBalanced的RestTemplate加上了uri的替换和负载均衡实现

总结

总的来说给出了两个扩展的口子

  • 1 通过实现LoadBalancerClient的reconstructURI方法来实现获取uri的方式
  • 2 service(服务名).ribbon.NFLoadBalancerRuleClassName来使用负载均衡的具体策略

你可能感兴趣的:(Java)