在上篇文章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
,兩個配置類,其中:
RibbonEagerLoadProperties
類中是關於Ribbon的飢餓加載模式的屬性ServerIntrospectorProperties
類中是關於安全端口的屬性這個配置類加載的類挺多的,可是比較重要的有這幾個:ide
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
微服務
這個類裏註冊的幾個bean就比較核心了ui
LoadBalancerInterceptor
客戶端請求攔截器this
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請求