在SC项目中,我们需要使用负载均衡方式的restTemplate 只需要在RestTemplate的Bean上加上@LoadBalanced注解即可,非常方便,这个是怎么做到的呢 (笔者之前也很好奇)
首先我们看下@LoadBalanced定义
/**
* Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient
* @author Spencer Gibb
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
从注释中就能看出是特意用来标记restTemplate使之配置使用LoadBalancerClient,而LoadBalancerClient又是什么呢
public interface LoadBalancerClient extends ServiceInstanceChooser {
T execute(String serviceId, LoadBalancerRequest request) throws IOException;
T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException;
URI reconstructURI(ServiceInstance instance, URI original);
}
LoandBalancerClient是一个接口,具体作用暂时不管,后面会重点分析,我们先来看 @LoadBalanced如何工作的
全局搜索下 @LoadBalanced 会发现有这样一个类 LoadBalancerAutoConfiguration,它有个属性restTemplates
@LoadBalanced
@Autowired(required = false)
private List restTemplates = Collections.emptyList();
那么@LoadBalanced和@Autowried结合使用的效果呢,意思就是这里注入的RestTempate Bean是所有加有
@LoadBalanced注解标记的,听起来有点绕口,这里我直接给出我们项目中的使用方式
@LoadBalanced
@Qualifier("loadBalancedRestTemplate")
@Bean(name = "loadBalancedRestTemplate")
@ConditionalOnMissingBean(name = "loadBalancedRestTemplate")
@ConditionalOnClass({ RestTemplate.class, HttpClient.class, RibbonLoadBalancerClient.class })
public LoadBalancedRestTemplate myRestTemplate() {
LoadBalancedRestTemplate restTemplate = new LoadBalancedRestTemplate(createFactory(), remoteClientProperties);
this.setConverterList(restTemplate);
List ins = restTemplate.getInterceptors();
ins.add(new EyasHeaderInterceptor(securityProperties, getRequestSourceApp()));
restTemplate.setInterceptors(ins);
return restTemplate;
}
我们定义了一个LoadBalancedRestTemplate bean 加上了@LoadBalanced注解,在restTemplates属性注入时候就会把
loadBalancedRestTemplate 注入进来,有什么用呢,接着看 LoadBalancerAutoConfiguration类
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
final List customizers) {
return new SmartInitializingSingleton() {
@Override
public void afterSingletonsInstantiated() {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
}
};
}
@Autowired(required = false)
private List transformers = Collections.emptyList();
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
}
@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate restTemplate) {
List list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
};
}
}
这里可以看出,会对我们加上@LoadBalanced注解的bean 添加loadBalancerInterceptor拦截器
我们看下拦截器的实现
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
private LoadBalancerClient loadBalancer;
private LoadBalancerRequestFactory requestFactory;
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
this.loadBalancer = loadBalancer;
this.requestFactory = requestFactory;
}
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
// for backwards compatibility
this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
}
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
}
}
重点看intercept方法 当我们restTemplate执行请求操作时,就会被拦截器拦截进入intercept方法
而loadBalancer是LoadBalancerClient的具体实现 我们SC项目中使用 RibbonLoadBalancerClient类 具体方法实现
@Override
public T execute(String serviceId, LoadBalancerRequest request) throws IOException {
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
serviceId), serverIntrospector(serviceId).getMetadata(server));
return execute(serviceId, ribbonServer, request);
}
第一行:根据serviceId 获取对应的loadBalancer
第二行:根据loadBalancer获取具体的server(这里根据负载均衡规则,获取到具体的服务实例)
第三行:创建ribbonServer
第四行:执行具体请求
到处粗略的分析了 SC中@LoadBalanced使用负载均衡方式的restTemplate原理,后续有时间会进一步补充说明