在上篇文章Ribbon架构剖析中,咱们已经介绍了Ribbon的架构组成以及不少重要的对象,相信你已经对Ribbon已经有一个清晰的认识了。本篇文章则研究一下Ribbon的原理算法
首先咱们知道,在普通项目中Ribbon的使用是这样的spring
@SpringBootApplication @RibbonClient(name = "provider-demo", configuration = cn.org.config.LoadBalanced.class) public class CloudDemoConsumerApplication { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(CloudDemoConsumerApplication.class, args); } }
这里面最引人瞩目的就是注解@RibbonClient
了,看一下这个注解都是作了什么吧缓存
@RibbonClient
观察@RibbonClient
的源码可知,这个注解使用@Import
注解引入了配置类RibbonClientConfigurationRegistrar
,看一下这个类的registerBeanDefinitions
方法安全
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { Map<String, Object> attrs = metadata.getAnnotationAttributes( RibbonClients.class.getName(), true); if (attrs != null && attrs.containsKey("value")) { AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value"); for (AnnotationAttributes client : clients) { registerClientConfiguration(registry, getClientName(client), client.get("configuration")); } } if (attrs != null && attrs.containsKey("defaultConfiguration")) { String name; if (metadata.hasEnclosingClass()) { name = "default." + metadata.getEnclosingClassName(); } else { name = "default." + metadata.getClassName(); } registerClientConfiguration(registry, name, attrs.get("defaultConfiguration")); } Map<String, Object> client = metadata.getAnnotationAttributes( RibbonClient.class.getName(), true); String name = getClientName(client); if (name != null) { registerClientConfiguration(registry, name, client.get("configuration")); } }
@RibbonClients
,注意,这里但是多了一个s的@RibbonClients
注解上是否存在属性value
和defaultConfiguration
,若是存在的话分别注册他们@RibbonClient
注解RibbonClientConfigurationRegistrar
这个类应该是能够同时处理这两个注解的,观察一下@RibbonClients
注解的源码发现它确实是引入的也是这个类RibbonClientSpecification
,这里留意一下private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { BeanDefinitionBuilder builder = BeanDefinitionBuilder .genericBeanDefinition(RibbonClientSpecification.class); builder.addConstructorArgValue(name); builder.addConstructorArgValue(configuration); registry.registerBeanDefinition(name + ".RibbonClientSpecification", builder.getBeanDefinition()); }
上方看完这些代码以后,咱们了解了@RibbonClients
和@RibbonClient
两个注解,能够对总体的流程仍是有些疑惑。那么接下来就看看自动装配都是作了什么吧架构
查看Ribbon包下的spring.factories
文件,发现引入了一个配置类RibbonAutoConfiguration
,那么从这个类开始看起吧负载均衡
@ConditionalOnClass
,当前环境必须存在这几个类: IClient
, RestTemplate
, AsyncRestTemplate
, Ribbon
@RibbonClients
,这个注解刚才已经讲过了,暂且不提@AutoConfigureAfter
,负载均衡确定是要基于注册中心来作的,因此自动装配是在Eureka初始化完毕以后初始化的@AutoConfigureBefore
,这里的两个类先不说,保持神秘@EnableConfigurationProperties
,两个配置类,其中:ide
RibbonEagerLoadProperties
类中是关于Ribbon的饥饿加载模式的属性ServerIntrospectorProperties
类中是关于安全端口的属性这个配置类加载的类挺多的,可是比较重要的有这几个:微服务
SpringClientFactory
,咱们知道每个微服务在都会调用多个微服务,而调用各个微服务的配置多是不同的,因此就须要这个建立客户端负载均衡器的工厂类,它能够为每个ribbon客户端生成不一样的Spring上下文,而观察这个类的configurations
属性也验证了这一点@Autowired(required = false) private List<RibbonClientSpecification> configurations = new ArrayList<>(); @Bean public SpringClientFactory springClientFactory() { SpringClientFactory factory = new SpringClientFactory(); factory.setConfigurations(this.configurations); return factory; }
RibbonLoadBalancerClient
,持有SpringClientFactory
对象,固然,它还有其余的功能,这里暂且不提上方虽然看了Ribbon的自动装配功能,可是好像离真相还有一些距离,这是由于虽然Ribbon准备好了,可是负载均衡还没看呢。SpringCloud把负载均衡相关的自动配置放在了spring-cloud-commons包下
负载均衡的配置类是LoadBalancerAutoConfiguration
ui
这个类里注册的几个bean就比较核心了this
LoadBalancerInterceptor
客户端请求拦截器
RestTemplateCustomizer
用于给全部的RestTemplate
增长拦截器
@Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return restTemplate -> { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; }
如今咱们就能够猜想,整个核心应该就是在这个拦截器上了,看一看拦截器的核心方法:
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)); }
其中requestFactory.createRequest(request, body, execution)
方法是为了把请求参数封装为request
重点关注execute
方法
public <T> T execute(String serviceId, LoadBalancerRequest<T> 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); }
咱们知道,每一个Ribbon客户端的负载均衡器都是惟一的,第一行getLoadBalancer
就会去建立这个负载均衡器
protected ILoadBalancer getLoadBalancer(String serviceId) { return this.clientFactory.getLoadBalancer(serviceId); } public ILoadBalancer getLoadBalancer(String name) { return getInstance(name, ILoadBalancer.class); } public <C> C getInstance(String name, Class<C> type) { C instance = super.getInstance(name, type); if (instance != null) { return instance; } IClientConfig config = getInstance(name, IClientConfig.class); return instantiateWithConfig(getContext(name), type, config); }
最后的逻辑是若是存在缓存则从缓存中获取,若是不存在建立
static <C> C instantiateWithConfig(AnnotationConfigApplicationContext context, Class<C> clazz, IClientConfig config) { C result = null; try { Constructor<C> constructor = clazz.getConstructor(IClientConfig.class); result = constructor.newInstance(config); } catch (Throwable e) { // Ignored } if (result == null) { result = BeanUtils.instantiate(clazz); if (result instanceof IClientConfigAware) { ((IClientConfigAware) result).initWithNiwsConfig(config); } if (context != null) { context.getAutowireCapableBeanFactory().autowireBean(result); } } return result; }
建立的大题流程则就是经过文章开始提到的两个注解注册的几个RibbonClientSpecification
类型的配置来建立
getServer
方法的实现应该能够猜出来,使用具体的负载均衡器结合相应的负载均衡算法再加上服务列表过滤、服务健康检测等操做最后会获取的一个可用服务
这里在调用以前把服务封装成了RibbonServer
private final String serviceId; private final Server server; private final boolean secure; private Map<String, String> metadata;
除了这几个属性外,RibbonServer
还有一个方法
public URI getUri() { return DefaultServiceInstance.getUri(this); }
这个方法就把服务从实例id转化为一个可调用的url了
public static URI getUri(ServiceInstance instance) { String scheme = (instance.isSecure()) ? "https" : "http"; String uri = String.format("%s://%s:%s", scheme, instance.getHost(), instance.getPort()); return URI.create(uri); }
而后就是发送http请求