学习springcluod的时候,有一个困惑,为什么RestTemplate上面@LoadBalanced注解,就能实现负载均衡,今天我们一起学习下源码,探索下springCloud底层的秘密:
第一步:在看源码之前我们先自己搭建一个消费者微服务(因为我们这里主要讲解的是springCloud的Ribbon负载均衡,所以注册中心和提供者这里就不再讲解了)
1、引入必要的maven依赖:
<parent> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-parentartifactId> <version>1.5.9.RELEASEversion> <relativePath /> parent> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-dependenciesartifactId> <version>Edgware.RELEASEversion> <type>pomtype> <scope>importscope> dependency> dependencies> dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-eurekaartifactId> dependency> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-ribbonartifactId> dependency> dependencies>
2、创建springboot的启动类:
@SpringBootApplication @EnableDiscoveryClient public class HelloApplicaton {
//这里就是创建一个负载均衡的RestTemplate Bean @LoadBalanced @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(HelloApplicaton.class, args); } }
3、resource下配置启动文件:application.yml或者application.properties(具体的配置文件的作用我觉得应该不用我做解释)
server: port: 9091 spring: application: name: post-service eureka: instance: hostname: localhost client: serviceUrl: defaultZone: http://localhost:1111/eureka/
4、创建消费者请求接口:
@RestController public class HelloController {
//注入前面创建的负载均衡的RestTemplate @Autowired public RestTemplate restTemplate; @RequestMapping(value = "/hello", method = RequestMethod.GET) public String postHello(){ //使用有负载均衡能力的RestTemplate请求微服务 return restTemplate.getForEntity("http://HELLO-SERVICE/hello" ,String.class,"").getBody(); } }
注:这里的HELLO-SERVICE就是注册中心存在的微服务的名称,这个大家应该都很清楚;
这样一个具有负载均衡能力的消费者接口就创建成功了!
但是为什么被@LoadBalanced注解修饰的RestTemplate就有了负载均衡的能力呢,先看看@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让其拥有负载均衡的能力,查看org.springframework.cloud.client.loadbalancer包下面的class,我们很容易发现LoadBalancerAutoConfiguration这个类;
@Configuration//这是一个配置类 @ConditionalOnClass(RestTemplate.class)//这个配置文件加载必要条件是存在RestTemplate类和LoadBalancerClient //Bean @ConditionalOnBean(LoadBalancerClient.class) @EnableConfigurationProperties(LoadBalancerRetryProperties.class) public class LoadBalancerAutoConfiguration {
//这个很重要,这里的restTemplates是所有的被@LoadBalanced注解的集合,这就是标记注解的作用(Autowired是可以集合注入的) @LoadBalanced @Autowired(required = false) private ListrestTemplates = Collections.emptyList(); @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); }
//生成一个LoadBalancerInterceptor的Bean @Configuration @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate") static class LoadBalancerInterceptorConfig { @Bean public LoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) { return new LoadBalancerInterceptor(loadBalancerClient, requestFactory); }
//给注解了@LoadBalanced的RestTemplate加上拦截器 @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { Listlist = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } }
}
总结:现在我们应该大致知道@loadBalanced的作用了,就是起到一个标记RestTemplate的作用,当服务启动时,标记了的RestTemplate对象里面就会被自动加入LoadBalancerInterceptor拦截器,这样当RestTemplate像外面发起http请求时,会被LoadBalancerInterceptor的intercept函数拦截,而intercept里面又调用了LoadBalancerClient接口实现类execute方法,我们接着往下看;
LoadBalancerInterceptor的intercept方法:
@Override public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
//这是以服务名为地址的原始请求:例:http://HELLO-SERVICE/hello 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)); }
这里的LoadBalancerClient的实现是RibbonLoadBalancerClient,
publicT execute(String serviceId, LoadBalancerRequest request) throws IOException {
//通过serviceId找到ILoadBalancer 的实现者,默认是ZoneAwareLoadBalancer
//(RibbonClientConfiguration里面对ILoadBalancer的实现做的默认配置)ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId); Server server = this.getServer(loadBalancer); if (server == null) { throw new IllegalStateException( "No instances available for " + serviceId); } else {
//将Server组装成RibbonServer,附加了一些额外的信息 RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server)); return this.execute(serviceId, ribbonServer, request); } }
//ZoneAwareLoadBalancer的Rule规则去选择Server(这里的规则是统一个zone的优先选择,这里返回的Server
//就是通过serviceId和Rule解析返回的带有IP:port形式的信息的Server)
protected Server getServer(ILoadBalancer loadBalancer) { return loadBalancer == null ? null : loadBalancer.chooseServer("default"); }
接着往下看:
publicT execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException { Server server = null; if (serviceInstance instanceof RibbonLoadBalancerClient.RibbonServer) { server = ((RibbonLoadBalancerClient.RibbonServer)serviceInstance).getServer(); } if (server == null) { throw new IllegalStateException("No instances available for " + serviceId); } else {
RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId); RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server); try {
//这一行很关键 T returnVal = request.apply(serviceInstance); statsRecorder.recordStats(returnVal); return returnVal; } catch (IOException var8) { statsRecorder.recordStats(var8); throw var8; } catch (Exception var9) { statsRecorder.recordStats(var9); ReflectionUtils.rethrowRuntimeException(var9); return null; } } }
public LoadBalancerRequestcreateRequest(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) { return new LoadBalancerRequest () { @Override public ClientHttpResponse apply(final ServiceInstance instance) throws Exception { HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer); if (transformers != null) { for (LoadBalancerRequestTransformer transformer : transformers) { serviceRequest = transformer.transformRequest(serviceRequest, instance); } } return execution.execute(serviceRequest, body); } }; }
这里需要注意ServiceRequestWrapper对象改写了getURL方法:
@Override public URI getURI() { URI uri = this.loadBalancer.reconstructURI( this.instance, getRequest().getURI()); return uri; }
所以这里的reconstructURI实际上调用的是实现类RibbonLoadBalancerClient的reconstructURI方法:
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还是以IP:port的形式 Server server = new Server(instance.getHost(), instance.getPort()); IClientConfig clientConfig = this.clientFactory.getClientConfig(serviceId); ServerIntrospector serverIntrospector = this.serverIntrospector(serviceId);
//是否需要转换为Https URI uri = RibbonUtils.updateToHttpsIfNeeded(original, clientConfig, serverIntrospector, server);
// 根据Server组装真正的URI
return context.reconstructURIWithServer(server, uri);
}
execution.execute(serviceRequest, body)方法调用的是InterceptingRequestExecution的execute方法:
@Override public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException { if (this.iterator.hasNext()) { ClientHttpRequestInterceptor nextInterceptor = this.iterator.next(); return nextInterceptor.intercept(request, body, this); } else { ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod()); for (Map.Entry> entry : request.getHeaders().entrySet()) { List values = entry.getValue(); for (String value : values) { delegate.getHeaders().add(entry.getKey(), value); } } if (body.length > 0) { StreamUtils.copy(body, delegate.getBody()); }
//这就是真正请求外部的地方
return delegate.execute();
}
}
}
这里的request.getURI()调用的就是
ServiceRequestWrapper的getURL,也就是RibbonLoadBalancerClient的
reconstructURI方法;总结:好了,至此springCloud的分析告一段落,这里也只是给给出了负载均衡的大纲,没有列出详细的细节,读者可根据大纲对照着查看细节,感谢您的观看,欢迎指错;