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

在這裏插入圖片描述

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來使用負載均衡的具體策略
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章