我們知道在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進行了包裝
- 1> this.loadBalancer.execute方法我們來看RibbonLoadBalancerClient的實現
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來使用負載均衡的具體策略