前言

前面我们分析了Eureka的源码,接下来这一章我们来研究一下Ribbon,本篇文章主要是对Ribbon的相关组件做一个认识,以及它的初始化配置做一个分析。

Ribbon的自动配置RibbonAutoConfiguration

在spring-cloud-netflix-ribbon-2.0.1.RELEASE.jar包的META-INF目录中有这么一个文件spring.factories,内容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration

当SpringBoot程序启动,自动配置扫描会加载该类RibbonAutoConfiguration注册到Spring容器,于是该自动配置类中的配置生效,那么该类配置了什么呢?

/*** Auto configuration for Ribbon (client side load balancing).** @author Spencer Gibb* @author Dave Syer* @author Biju Kunjummen*/
@Configuration
//条件
@ConditionalOnClass({ IClient.class, RestTemplate.class, AsyncRestTemplate.class, Ribbon.class})
@RibbonClients
//在EurekaClientAutoConfiguration之后配置
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
//在LoadBalancerAutoConfiguration之前配置
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
//载入迫切加载配置
@EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {//RibbonClient客户端指定的配置,有多少个Ribbon客户端这里就会有多少个RibbonClientSpecification@Autowired(required = false)private List<RibbonClientSpecification> configurations = new ArrayList<>();//饥饿加载配置@Autowiredprivate RibbonEagerLoadProperties ribbonEagerLoadProperties;@Beanpublic HasFeatures ribbonFeature() {return HasFeatures.namedFeature("Ribbon", Ribbon.class);}// 创建RibbonClient的ApplicationContext上下文,并创建RibbonClient相关组件如IClient、ILoadbalancer等@Beanpublic SpringClientFactory springClientFactory() {SpringClientFactory factory = new SpringClientFactory();factory.setConfigurations(this.configurations);return factory;}//注册了LoadBalancerClient ,负载均衡客户端,很重要的一个类@Bean@ConditionalOnMissingBean(LoadBalancerClient.class)public LoadBalancerClient loadBalancerClient() {return new RibbonLoadBalancerClient(springClientFactory());}//创建LoadBalancedRetryPolicy的工厂,负载均衡重试功能@Bean//重试条件,必须配置RetryTemplate@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")@ConditionalOnMissingBeanpublic LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(final SpringClientFactory clientFactory) {return new RibbonLoadBalancedRetryFactory(clientFactory);}@Bean@ConditionalOnMissingBeanpublic PropertiesFactory propertiesFactory() {return new PropertiesFactory();}//如果配置了eager-load饥饿加载,就注册RibbonApplicationContextInitializer 上下文初始化对象,//然后在ApplicationReadyEvent事件之后会立马初始化上下文@Bean@ConditionalOnProperty(value = "ribbon.eager-load.enabled")public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {return new RibbonApplicationContextInitializer(springClientFactory(),ribbonEagerLoadProperties.getClients());}//Ribbon的http请求配置@Configuration@ConditionalOnClass(HttpRequest.class)@ConditionalOnRibbonRestClientprotected static class RibbonClientHttpRequestFactoryConfiguration {@Autowiredprivate SpringClientFactory springClientFactory;//RestTemplate定制器@Beanpublic RestTemplateCustomizer restTemplateCustomizer(final RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory) {return restTemplate -> restTemplate.setRequestFactory(ribbonClientHttpRequestFactory);}//注册RibbonClientHttpRequestFactory ,听过它来创建ClientHttpRequest用来发http请求的,后续Ribbon执行流程中会用到ClientHttpRequest@Beanpublic RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory() {return new RibbonClientHttpRequestFactory(this.springClientFactory);}}//TODO: support for autoconfiguring restemplate to use apache http client or okhttp@Target({ ElementType.TYPE, ElementType.METHOD })@Retention(RetentionPolicy.RUNTIME)@Documented@Conditional(OnRibbonRestClientCondition.class)@interface ConditionalOnRibbonRestClient { }private static class OnRibbonRestClientCondition extends AnyNestedCondition {public OnRibbonRestClientCondition() {super(ConfigurationPhase.REGISTER_BEAN);}@Deprecated //remove in Edgware"@ConditionalOnProperty("ribbon.http.client.enabled")static class ZuulProperty {}@ConditionalOnProperty("ribbon.restclient.enabled")static class RibbonProperty {}}
}

解释一下:

  • RibbonClient: Ribbon的客户端,比如A服务调用了B服务和C服务,那么Ribbon就会创建B服务的客户端和C服务的客户端

  • SpringClientFactory:用来给Ribbon客户端创建上下文和配置,它为每个客户机名称创建一个Spring ApplicationContext,并创建好RibbonClient客户端相关的bean和配置,如:ILoadBalancer,IClientConfig,RibbonLoadBalancerContext。注意:是会为每个客户端都会做一个套配置

  • LoadBalancerClient :负载均衡客户端,RibbonLoadBalancerClient是对LoadBalancerClient的实现,exec方法包含了负载均衡的功能,Ribbon做负载均衡时用的就是它【重要】

  • RibbonEagerLoadProperties:用来加载“饥饿加载”配置ribbon.eager-load.enabled,指定些哪些RibonClient需要迫切初始化

  • RibbonApplicationContextInitializer:配置了ribbon.eager-load.enabled的客户端在系统启动的时候就会初始化RibbonClient上下文和配置信息,默认情况下RibbonClient的上下文和配置是在调用的时候才进行初始化的,配置了饥饿加载,项目刚启动时可以减少服务调用失败的情况。

  • PropertiesFactory:加载配置的工厂,主要负责加载Ribbon客户端相关的配置类:ILoadBalancer,IPing,IRule,ServerList,ServerListFilter:

    public PropertiesFactory() {classToProperty.put(ILoadBalancer.class, "NFLoadBalancerClassName");classToProperty.put(IPing.class, "NFLoadBalancerPingClassName");classToProperty.put(IRule.class, "NFLoadBalancerRuleClassName");classToProperty.put(ServerList.class, "NIWSServerListClassName");classToProperty.put(ServerListFilter.class, "NIWSServerListFilterClassName");
    }
    

SpringClientFactory Ribbon上下文初始化

SpringClientFactory用来给Ribbon客户端创建上下文和配置,它为每个客户机名称创建一个Spring ApplicationContext,并创建好RibbonClient客户端相关的bean和配置,如:ILoadBalancer,IClientConfig,RibbonLoadBalancerContext。

public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {static final String NAMESPACE = "ribbon";public SpringClientFactory() {//ribbon的客户端配置RibbonClientConfiguration,Ribbon默认配置类的类型super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");}/**这个方法的作用是从Ribbon的客户端上下文对象中(AnnotationConfigApplicationContext)根据name和type获取一个Bean的实例,比如:IloadBalancer , * Get the rest client associated with the name.* @throws RuntimeException if any error occurs*/public <C extends IClient<?, ?>> C getClient(String name, Class<C> clientClass) {return getInstance(name, clientClass);}/**根据Ribbon客户端的服务名,获取客户端的ILoadBalancer对象* Get the load balancer associated with the name.* @throws RuntimeException if any error occurs*/public ILoadBalancer getLoadBalancer(String name) {return getInstance(name, ILoadBalancer.class);}/**根据Ribbon客户端的服务名,获取客户端的配置对象,默认实现是DefaultClientConfigImpl就是家长Ribbon开头的配置* Get the client config associated with the name.* @throws RuntimeException if any error occurs*/public IClientConfig getClientConfig(String name) {return getInstance(name, IClientConfig.class);}/**根据Ribbon客户端的服务名,获取RibbonLoadBalancerContext 负载均衡器的上下文对象* Get the load balancer context associated with the name.* @throws RuntimeException if any error occurs*/public RibbonLoadBalancerContext getLoadBalancerContext(String serviceId) {return getInstance(serviceId, RibbonLoadBalancerContext.class);}//根据配置,创建一个实例,clazz是要创建的对象的字节码,config是对象需要的 配置static <C> C instantiateWithConfig(Class<C> clazz, IClientConfig config) {return instantiateWithConfig(null, clazz, 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;}//获取实例,name是Ribbon客户端的服务名,type是类型,//创建IClientConfig,ILoadBalancer,getClient,RibbonLoadBalancerContext都要调用这个方法@Overridepublic <C> C getInstance(String name, Class<C> type) {//调用父类NamedContextFactory的getInstance实例化对象,如果为空,就根据IClientConfig客户端配置创建一个实例C instance = super.getInstance(name, type);if (instance != null) {return instance;}IClientConfig config = getInstance(name, IClientConfig.class);//得到获取上下文对象,然后创建实例return instantiateWithConfig(getContext(name), type, config);}//获取Spring上下文对象@Overrideprotected AnnotationConfigApplicationContext getContext(String name) {return super.getContext(name);}

这里配置了什么呢?

  • 1.SpringClientFactory中提供了 getClient,getLoadBalancer,getClientConfig,getLoadBalancerContext 等方法,
  • 2.这些方法都需要调用getInstance得到相关实例,而getInstance方法中向会去从super.getInstance(其实就是通过AnnotationConfigApplicationContext.getBean 上下文对象)获取Ben
  • 3.如果super.getInstance获取不到bean,就先调用getContext方法得到上下文对象,然后调用instantiateWithConfig方法根据IClientConfig配置使用反射创建一个实例:instantiateWithConfig(getContext(name), type, config);

我们看下其父类NamedContextFactory做了些什么事情

//父类源码
public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>implements DisposableBean, ApplicationContextAware {public interface Specification {String getName();Class<?>[] getConfiguration();}//保存上下文对象的mapprivate Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();//保存配置的mapprivate Map<String, C> configurations = new ConcurrentHashMap<>();public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName,String propertyName) {//defaultConfigType是默认的配置类型this.defaultConfigType = defaultConfigType;this.propertySourceName = propertySourceName;this.propertyName = propertyName;}//设置上下文对象@Overridepublic void setApplicationContext(ApplicationContext parent) throws BeansException {this.parent = parent;}//添加配置到configurations map中,public void setConfigurations(List<C> configurations) {for (C client : configurations) {this.configurations.put(client.getName(), client);}}//获取上下文名字集合public Set<String> getContextNames() {return new HashSet<>(contexts.keySet());}//销毁,上下文对象关闭,清理上下文map@Overridepublic void destroy() {Collection<AnnotationConfigApplicationContext> values = this.contexts.values();for (AnnotationConfigApplicationContext context : values) {// This can fail, but it never throws an exception (you see stack traces// logged as WARN).context.close();}this.contexts.clear();}//【重要】根据Ribbon客户端名字获取上下文对象,通过这个上下文对象就能获取Ribbon客户端所需要的Bean(IClicentConfig,ILoadBalancer )protected AnnotationConfigApplicationContext getContext(String name) {if (!this.contexts.containsKey(name)) {synchronized (this.contexts) {if (!this.contexts.containsKey(name)) {//如果contexts map中没有当前Ribbon客户端对应的上下文,就调用createContext先创建this.contexts.put(name, createContext(name));}}}//根据名字获取contexts中的上下对象return this.contexts.get(name);}//根据名字创建上下文protected AnnotationConfigApplicationContext createContext(String name) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();//注册指定的的RibbonClient的配置类,比如通过@RibbonClient注解指定的配置类if (this.configurations.containsKey(nam) {for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {context.register(configuration);}}//注册所有default开头的Ribbon配置到上下文对象,即所有RibbonClient的默认配置for (Map.Entry<String, C> entry : this.configurations.entrySet()) {if (entry.getKey().startsWith("default.")) {for (Class<?> configuration : entry.getValue().getConfiguration()) {context.register(configuration);}}}//注册了默认的配置类,defaultConfigType就是RibbonClientConfigurationcontext.register(PropertyPlaceholderAutoConfiguration.class,this.defaultConfigType);context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,Collections.<String, Object> singletonMap(this.propertyName, name)));if (this.parent != null) {// Uses Environment from parent as well as beanscontext.setParent(this.parent);}context.setDisplayName(generateDisplayName(name));//刷新容器context.refresh();return context;}protected String generateDisplayName(String name) {return this.getClass().getSimpleName() + "-" + name;}//从上下文对象中获取实例public <T> T getInstance(String name, Class<T> type) {//根据名字先得到上下文对象,从上下文对象中根据type获取BeanAnnotationConfigApplicationContext context = getContext(name);if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,type).length > 0) {return context.getBean(type);}return null;}//从上下文对象中获取实例public <T> Map<String, T> getInstances(String name, Class<T> type) {AnnotationConfigApplicationContext context = getContext(name);if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,type).length > 0) {return BeanFactoryUtils.beansOfTypeIncludingAncestors(context, type);}return null;}

NamedContextFactory主要提供了

  • getContext(String name) : 根据客户端名字从 Map<String, AnnotationConfigApplicationContext> contexts 中获取上下文对象的方法,如果contexts中没有上下文会调用createContext(name) 创建上下文,然后放入contexts中再返回
  • createContext(String name) :根据客户端名字创建上下文对象,方法中分别注册了RibbonClient客户端配置类,default开头的所有RibbonClient的默认配置,以及RibbonClientConfiguration全局默认配置,然后刷新容器,返回上下文对象
  • getInstance(String name, Class type) :根据名字和类型返回实例,方法先根据名字得到上下文对象然后从上下文对象中获取Bean

这里有两个问题,一是configurations配置是从哪儿来的,二是createContext在什么时候调用?

一是configurations配置是从哪儿来的?这个我们看哪儿在给这个Map添加数据

 //存储配置的mapprivate Map<String, C> configurations = new ConcurrentHashMap<>();//添加配置public void setConfigurations(List<C> configurations) {for (C client : configurations) {//map添加配置this.configurations.put(client.getName(), client);}}

这个setConfigurations方法是在RibbonAutoConfiguration配置被调用

public class RibbonAutoConfiguration {//注入RibbonClientSpecification@Autowired(required = false)private List<RibbonClientSpecification> configurations = new ArrayList<>();...省略...@Beanpublic SpringClientFactory springClientFactory() {SpringClientFactory factory = new SpringClientFactory();//给SpringClientFactory 指定配置集合factory.setConfigurations(this.configurations);return factory;}

RibbonClientConfigurationRegistrar Ribbon客户端配置注册器

configurations 是在RibbonAutoConfiguration 配置类中注入进来的,那么RibbonClientSpecification是在什么时候注册到Spring容器中的呢?有个RibbonClientConfigurationRegistrar专门负责注册RibbonClientConfiguration

//RibbonClientConfiguration注册器
public class RibbonClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {//获取@RibbonClients的注解属性,value对应的是@RibbonClientMap<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) {//获取到每一个@RibbonClient,从注解中获取configuration配置,进行注册registerClientConfiguration(registry, getClientName(client),client.get("configuration"));}}//这里是判断是否有默认的配置@RibbonClients注解的defaultConfigurationif (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"));}//这里获取的是@RibbonClient的configuration配置Map<String, Object> client = metadata.getAnnotationAttributes(RibbonClient.class.getName(), true);String name = getClientName(client);if (name != null) {registerClientConfiguration(registry, name, client.get("configuration"));}}private String getClientName(Map<String, Object> client) {if (client == null) {return null;}String value = (String) client.get("value");if (!StringUtils.hasText(value)) {value = (String) client.get("name");}if (StringUtils.hasText(value)) {return value;}throw new IllegalStateException("Either 'name' or 'value' must be provided in @RibbonClient");}//注册了客户端的配置RibbonClientSpecificationprivate void registerClientConfiguration(BeanDefinitionRegistry registry,Object name, Object configuration) {//生成RibbonClientSpecificationBeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(RibbonClientSpecification.class);builder.addConstructorArgValue(name);builder.addConstructorArgValue(configuration);registry.registerBeanDefinition(name + ".RibbonClientSpecification",builder.getBeanDefinition());}}

这里解析了@RibbonClients标签和@RibbonClient 标签,得到相关配置类后注册到Spring容器中,生成RibbonClientSpecification对象,那么在RibbonAutoConfiguration 中就可以@Autowired进来,然后添加到NamedContextFactory的configurations Map中,从而在常见RibbonClient上下文的时候就可以设置相应的Config配置。

RibbonApplicationContextInitializer Ribbon饥饿初始化

还有一个问题就是NamedContextFactory.createContext上下文在什么时候调用?一方面是在获取上下文的时候,如果获取不到会先调用createContext方法创建,还有一个地方是在RibbonApplicationContextInitializer#initialize 方法中会去调用createContext创建上下文,而RibbonApplicationContextInitializer就是负责Ribbon上下文的初始化,他在 RibbonAutoConfiguration中被定义

@EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {//初始化Ribbon上下文,如果配置了ribbon.eager-load.enabled=true,在系统启动时就会初始化@Bean@ConditionalOnProperty(value = "ribbon.eager-load.enabled")public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {//从ribbonEagerLoadProperties配置文件中获取Ribbon客户端名字return new RibbonApplicationContextInitializer(springClientFactory(),ribbonEagerLoadProperties.getClients());}
}

RibbonApplicationContextInitializer是怎么做的呢

public class RibbonApplicationContextInitializerimplements ApplicationListener<ApplicationReadyEvent> {//创建Ribbon客户端上下文的工厂类private final SpringClientFactory springClientFactory;//ribbon的客户端名字结合//List of Ribbon client namesprivate final List<String> clientNames;public RibbonApplicationContextInitializer(SpringClientFactory springClientFactory,List<String> clientNames) {this.springClientFactory = springClientFactory;//Ribbon客户端名字集合this.clientNames = clientNames;}//初始化,为每个客户端初始化,springClientFactory.getContext方法中会先调用createContext方法protected void initialize() {if (clientNames != null) {for (String clientName : clientNames) {this.springClientFactory.getContext(clientName);}}}//Spring容器事件,容器准备好就会调用该方法,然后调用initialize();
初始化@Overridepublic void onApplicationEvent(ApplicationReadyEvent event) {initialize();}
}

首先在RibbonAutoConfiguration 中通过RibbonEagerLoadProperties获取到配置的Ribbon客户端名字集合,交给RibbonApplicationContextInitializer初始化器,RibbonApplicationContextInitializer监听是Spring的ApplicationReadyEvent 事件,调用initialize方法,通过springClientFactory.getContext(clientName);
为每个客户端初始化上下文对象。

到这里我们就搞清楚了RibbonClient客户端的初始化流程了。

LoadBalancerAutoConfiguration 负载均衡器配置

LoadBalancerAutoConfiguration是针对于LoadBalancer负载均衡的配置类,它是在spring-cloud-commons-2.0.1.RELEASE.jar/META-INF/spring.factories 文件中被定义,SpringBoot启动自动配置时被加载

源码如下

@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {//被注解了@LoadBalanced的RestTemplate @LoadBalanced@Autowired(required = false)private List<RestTemplate> restTemplates = Collections.emptyList();//给RestTemplate初始化,customizer.customize(restTemplate)方法来给RestTemplate增加拦截器LoadBalancerInterceptor//该方法会调用下面的restTemplateCustomizer方法@Beanpublic SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {return () -> restTemplateCustomizers.ifAvailable(customizers -> {for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {for (RestTemplateCustomizer customizer : customizers) {//给RestTemplate增加拦截器LoadBalancerInterceptorcustomizer.customize(restTemplate);}}});}@Autowired(required = false)private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();//LoadBalancerRequestFactory用来创建LoadBalancerRequest,后续Ribbon发请求会用到LoadBalancerRequestFactory@Bean@ConditionalOnMissingBeanpublic LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {return new LoadBalancerRequestFactory(loadBalancerClient, transformers);}@Configuration@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")static class LoadBalancerInterceptorConfig {//LoadBalancerInterceptor 负载均衡器拦截器配置,//用来拦截RestTemplate的请求,从而执行exec方法实现负载均衡的功能@Beanpublic LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient,LoadBalancerRequestFactory requestFactory) {return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);}//给 RestTemplate 设置拦截器loadBalancerInterceptor,执行RestTemplate的时候会执行该拦截器@Bean@ConditionalOnMissingBeanpublic RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {return restTemplate -> {List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);restTemplate.setInterceptors(list);};}}

这里定义了

  • LoadBalancerRequestFactory :LoadBalancerRequest 负载均衡请求工厂,用来创建LoadBalancerRequest
  • LoadBalancerInterceptor :请求拦截器,拦截RestTemplate请求,调用exec方法实现负载均衡
  • RestTemplateCustomizer :定制RestTmplate,设置拦截器LoadBalancerInterceptor

我们看一下拦截器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 compatibilitythis(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));}//拦截方法,这里会调用loadBalancer.execute@Overridepublic 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));}
}

这个拦截器很重要,当RestTmplate发请求时,会调用该拦截器,intercept方法执行,该方法会通过requestFactory.createRequest创建LoadBalancerRequest请求对象,调用ILoadBalancer.exec方法实现负载均衡请求

RibbonClientConfiguration Ribbon客户端配置

除此之外还有一个配置类我们需要去关注一下:RibbonClientConfiguration Ribbon的客户端配置

/*** @author Dave Syer* @author Tim Ysewyn*/
@SuppressWarnings("deprecation")
@Configuration
@EnableConfigurationProperties
//这里的顺序很重要,last应该是默认值,first应该是可选的
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653//1.HttpClientConfiguration:定义了ApacheHttpClient 和 OkHttpClient
//2.OkHttpRibbonConfiguration :OkHttpClient配置
//3.RestClientRibbonConfiguration:创建一个 RestClient
//4.HttpClientRibbonConfiguration:Ribbon的http配置,定义了CloseableHttpClient;RibbonLoadBalancingHttpClient;RetryableRibbonLoadBalancingHttpClient@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {//默认链接超时public static final int DEFAULT_CONNECT_TIMEOUT = 1000;//默认读取超时public static final int DEFAULT_READ_TIMEOUT = 1000;@RibbonClientNameprivate String name = "client";// TODO: maybe re-instate autowired load balancers: identified by name they could be// associated with ribbon clients@Autowiredprivate PropertiesFactory propertiesFactory;//ribbon的配置,默认配置实现:DefaultClientConfigImpl@Bean@ConditionalOnMissingBeanpublic IClientConfig ribbonClientConfig() {DefaultClientConfigImpl config = new DefaultClientConfigImpl();config.loadProperties(this.name);config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);return config;}//配置负载均衡算法类,如果有自定义,使用自定义的,否则使用的是ZoneAvoidanceRule 区域选择@Bean@ConditionalOnMissingBeanpublic IRule ribbonRule(IClientConfig config) {//是否设置算法if (this.propertiesFactory.isSet(IRule.class, name)) {return this.propertiesFactory.get(IRule.class, config, name);}ZoneAvoidanceRule rule = new ZoneAvoidanceRule();rule.initWithNiwsConfig(config);return rule;}//定义IPing,用来检查服务是否可用的,默认使用DummyPing@Bean@ConditionalOnMissingBeanpublic IPing ribbonPing(IClientConfig config) {if (this.propertiesFactory.isSet(IPing.class, name)) {return this.propertiesFactory.get(IPing.class, config, name);}return new DummyPing();}//服务列表,定义用于获取服务器列表的方法的接口,默认实现是ConfigurationBasedServerList,很重要@Bean@ConditionalOnMissingBean@SuppressWarnings("unchecked")public ServerList<Server> ribbonServerList(IClientConfig config) {if (this.propertiesFactory.isSet(ServerList.class, name)) {return this.propertiesFactory.get(ServerList.class, config, name);}ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();//根据配置创建serverList.initWithNiwsConfig(config);return serverList;}//服务器列表更新器,默认实现是PollingServerListUpdater//是动态服务器列表更新程序更新的默认策略 @Bean@ConditionalOnMissingBeanpublic ServerListUpdater ribbonServerListUpdater(IClientConfig config) {return new PollingServerListUpdater(config);}//负载均衡器ILoadBalancer ,//默认实现ZoneAwareLoadBalancer,继承于DynamicServerListLoadBalancer@Bean@ConditionalOnMissingBeanpublic ILoadBalancer ribbonLoadBalancer(IClientConfig config,ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,IRule rule, IPing ping, ServerListUpdater serverListUpdater) {if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {return this.propertiesFactory.get(ILoadBalancer.class, config, name);}return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,serverListFilter, serverListUpdater);}//服务列表过滤器,默认ZonePreferenceServerListFilter 区域首选@Bean@ConditionalOnMissingBean@SuppressWarnings("unchecked")public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {if (this.propertiesFactory.isSet(ServerListFilter.class, name)) {return this.propertiesFactory.get(ServerListFilter.class, config, name);}ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();//根据配置创建filter.initWithNiwsConfig(config);return filter;}//负载均衡上下文@Bean@ConditionalOnMissingBeanpublic RibbonLoadBalancerContext ribbonLoadBalancerContext(ILoadBalancer loadBalancer,IClientConfig config, RetryHandler retryHandler) {return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler);}//默认负载均衡器重试处理程序@Bean@ConditionalOnMissingBeanpublic RetryHandler retryHandler(IClientConfig config) {return new DefaultLoadBalancerRetryHandler(config);}//服务内省器@Bean@ConditionalOnMissingBeanpublic ServerIntrospector serverIntrospector() {return new DefaultServerIntrospector();}@PostConstructpublic void preprocess() {setRibbonProperty(name, DeploymentContextBasedVipAddresses.key(), name);}//覆盖其他Rest客户端static class OverrideRestClient extends RestClient {private IClientConfig config;private ServerIntrospector serverIntrospector;protected OverrideRestClient(IClientConfig config,ServerIntrospector serverIntrospector) {super();this.config = config;this.serverIntrospector = serverIntrospector;initWithNiwsConfig(this.config);}//使用服务器重构URI@Overridepublic URI reconstructURIWithServer(Server server, URI original) {URI uri = updateToSecureConnectionIfNeeded(original, this.config,this.serverIntrospector, server);return super.reconstructURIWithServer(server, uri);}//初始化ApacheHttpClient4  客户端@Overrideprotected Client apacheHttpClientSpecificInitialization() {ApacheHttpClient4 apache = (ApacheHttpClient4) super.apacheHttpClientSpecificInitialization();apache.getClientHandler().getHttpClient().getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);return apache;}}}

RibbonClientConfiguration 配置了几个比较关键的组件

  • IClientConfig :Ribon的客户端配置对象
  • IRule:Ribbon的负载均衡策略,如果没有指定策略类,默认使用ZoneAvoidanceRule ,拥有zone选择和轮询算法
  • IPing :检查服务器是否可用,可用的服务器将作为Ribbon负载均衡的候选服务器
  • ServerList : 用来获取所有server的注册列表的接口,提供了初始化服务列表和更新服务列表的方法,DynamicServerListLoadBalancer通过它来获取服务注册表
  • ServerListUpdater : 服务列表更新器默认实现PollingServerListUpdater ,维护了一个定时器30s/次更新
  • ServerListFilter :服务列表过滤器,通过该接口过滤后的server列表作为负载均衡候选的服务列表,默认实现ZonePreferenceServerListFilter 首选根据区域过滤。
  • ILoadBalancer :负载均衡器,默认实现ZoneAwareLoadBalancer,Ribobn通过它实现负载均衡
  • LoadBalancerContext: Ribbon负载均衡器上下文对象,默认实现RibbonLoadBalancerContext
  • RetryHandler:重试处理器,它确定负载均衡器是否可重试,维护了Ribbon的重试次数
  • ServerIntrospector :服务器自省,可以确定服务器是安全访问,和获取服务的元数据

ILoadBalancer 负载均衡器

负载均衡器接口,Ribbon比较核心的一个组件负责调用IRule从候选的服务里列表选择一个服务

/**
接口定义了软件的负载均衡器的操作* Interface that defines the operations for a software loadbalancer. A typical* loadbalancer minimally need a set of servers to loadbalance for, a method to* mark a particular server to be out of rotation and a call that will choose a* server from the existing list of server.* * @author stonse* */
public interface ILoadBalancer {/**服务器的初始列表* Initial list of servers.* This API also serves to add additional ones at a later time* The same logical server (host:port) could essentially be added multiple times* (helpful in cases where you want to give more "weightage" perhaps ..)* * @param newServers new servers to add*/public void addServers(List<Server> newServers);/**从负载均衡器选择一个服务* Choose a server from load balancer.* * @param key An object that the load balancer may use to determine which server to return. null if *         the load balancer does not use this parameter.* @return server chosen*/public Server chooseServer(Object key);/**要通过负载平衡器的客户端调用,以通知服务器已关闭意外* To be called by the clients of the load balancer to notify that a Server is down* else, the LB will think its still Alive until the next Ping cycle - potentially* (assuming that the LB Impl does a ping)* * @param server Server to mark as down*/public void markServerDown(Server server);/**获取当前服务器列表,过时了* @deprecated 2016-01-20 This method is deprecated in favor of the* cleaner {@link #getReachableServers} (equivalent to availableOnly=true)* and {@link #getAllServers} API (equivalent to availableOnly=false).** Get the current list of servers.** @param availableOnly if true, only live and available servers should be returned*/@Deprecatedpublic List<Server> getServerList(boolean availableOnly);/**获取可用的服务列表,只有那些启动并可以连接的服务器* @return Only the servers that are up and reachable.*/public List<Server> getReachableServers();/**return所有已知服务器,可访问和不可访问* @return All known servers, both reachable and unreachable.*/public List<Server> getAllServers();
}

ILoadBalancer 负载均衡器接口包括了如下方法

  • chooseServer 根据服务名选择一个服务
  • addServers添加服务集合,
  • markServerDown标记服务下线,
  • getServerList获取可用的服务列表
  • getReachableServers获取可用的服务列表,
  • getAllServers获取所有的服务列表

至于Server对象是用来封装一个可寻址得到目标服务器

看一下ILoadBalancer 接口的继承体系如下

  • DynamicServerListLoadBalancer是动态服务器列表负载均衡器,具有动态获取服务器的候选列表的功能,即便注册表在修改也可以动态获取有效的服务
  • ZoneAwareLoadBalancer是区域感知负载均衡器,选择服务器时可以根据区域zone进行选择

IRule负载均衡策略

IRule就是具体的负载均衡算法接口,包括了存取负载均衡器方法和选择服务方法,它很多的实现,一个实现代表一种算法,是ribbon实现负载均衡的核心算法,IRule源码

/**
接口定义了负载均衡器“规则”。,众所周知的负载均衡策略,包括基于轮循,响应时间等* Interface that defines a "Rule" for a LoadBalancer. A Rule can be thought of* as a Strategy for loadbalacing. Well known loadbalancing strategies include* Round Robin, Response Time based etc.* * @author stonse* */
public interface IRule{/*选择服务的具体方法,从 allServers 或者 lb.upServers选择一个服务* choose one alive server from lb.allServers or* lb.upServers according to key* * @return choosen Server object. NULL is returned if none*  server is available */public Server choose(Object key);//设置负载均衡器public void setLoadBalancer(ILoadBalancer lb);//获取负载均衡器public ILoadBalancer getLoadBalancer();
}

他的实现类如下

  • ClientConfigEnabledRoundRobinRule :它使用的是RoundRobinRule的算法,即轮询
  • BestAvailableRule 跳过“短路”的服务器并选择并发请求最少的服务器的规则
  • PredicateBaseRule:基于Predicate的策略,继承ClientConfigEnabledRoundRobinRule 轮询算法
  • RandomRule :随机选择一个server
  • RoundRobinRule :轮询选择server
  • RetryRule :根据轮询的方式重试
  • WeightedResponseTimeRule : 根据响应时间去分配一个weight ,weight越低,被选择的可能性就越低
  • ZoneAvoidanceRule :根据server的zone区域和可用性来轮询选择

ServerList 服务列表获取接口

用来获取负载均衡候选的server的注册列表的接口,提供了初始化服务列表和更新服务列表的方法

/*** Interface that defines the methods sed to obtain the List of Servers* @author stonse** @param <T>*/
public interface ServerList<T extends Server> {//初始化服务列表public List<T> getInitialListOfServers();/*** Return updated list of servers. This is called say every 30 secs* (configurable) by the Loadbalancer's Ping cycle* */public List<T> getUpdatedListOfServers();   }

ServerList有一个比较重要的实现DiscoveryEnabledNIWSServerList,它可以通过服务发现的方式从获取服务注册表,后续会讲到。

ServerListUpdater 服务列表更新器

它提供了更新服务列表的功能,它有两个实现分别是 EurekaNotificationServerListUpdater 它利用eureka的事件侦听器触发服务列表更新,和 PollingServerListUpdater 采用定时任务定时更新服务列表,后面详细说。

ServerListFilter 服务列表过滤

它负责过滤负载均衡器候选的服务列表,在Rbbon加载到servers服务列表后就会使用ServerListFilter进行过滤,比如ZonePreferenceServerListFilter就是根据区域进行过滤

DynamicServerListLoadBalancer 负载均衡器候选服务列表的获取

首先说说ILoadBalancer的实现ZoneAwareLoadBalancer ,它是拥有区域选择功能的负载均衡器,继承了DynamicServerListLoadBalancer ,在负载均衡的时候Ribbon会 , 拥有服务注册表更新功能,这里我们主要来跟踪一下Ribbon是如何更新服务列表的

public class ZoneAwareLoadBalancer<T extends Server> extends DynamicServerListLoadBalancer<T> {void setUpServerList(List<Server> upServerList) {this.upServerList = upServerList;}public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule,IPing ping, ServerList<T> serverList, ServerListFilter<T> filter,ServerListUpdater serverListUpdater) {//调用super初始化                     super(clientConfig, rule, ping, serverList, filter, serverListUpdater);}

在ZoneAwareLoadBalancer构造器中调用了super(DynamicServerListLoadBalancer)进行初始化 super(clientConfig, rule, ping, serverList, filter, serverListUpdater);

public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {...省略...public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,ServerList<T> serverList, ServerListFilter<T> filter,ServerListUpdater serverListUpdater) {super(clientConfig, rule, ping);this.serverListImpl = serverList;this.filter = filter;this.serverListUpdater = serverListUpdater;if (filter instanceof AbstractServerListFilter) {((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());}restOfInit(clientConfig);}..省略...void restOfInit(IClientConfig clientConfig) {boolean primeConnection = this.isEnablePrimingConnections();// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()this.setEnablePrimingConnections(false);enableAndInitLearnNewServersFeature();//更新服务列表updateListOfServers();if (primeConnection && this.getPrimeConnections() != null) {this.getPrimeConnections().primeConnections(getReachableServers());}this.setEnablePrimingConnections(primeConnection);LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());}...省略...@VisibleForTestingpublic void updateListOfServers() {List<T> servers = new ArrayList<T>();if (serverListImpl != null) {//通过ServerList获取服务列表servers = serverListImpl.getUpdatedListOfServers();LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",getIdentifier(), servers);if (filter != null) {//通过过滤器过滤服务列表servers = filter.getFilteredListOfServers(servers);LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",getIdentifier(), servers);}}//更新所有的服务列表updateAllServerList(servers);}

DynamicServerListLoadBalancer的构造器中把相关的对象赋值好之后,调用了 restOfInit(clientConfig);方法进行初始化,然后调用updateListOfServers更新服务注册表,updateListOfServers方法中做了这几件事情

  • servers = serverListImpl.getUpdatedListOfServers():通过ServerList方法获取服务列表
  • servers = filter.getFilteredListOfServers(servers):如果服务列表servers不为空,调用ServerListFilter过滤服务
  • 然后调用updateAllServerList(servers);方法把所有的server的Alive可用状态设置为true

对于restOfInit方法在initWithNiwsConfig方法中也会被调用,这个我们后面再说,我们重点跟踪一下ServerList#getUpdatedListOfServers方法看他是如何获取到服务注册表的 , 我这里跟踪了一下断点,serverListImpl.getUpdatedListOfServers()代码来到实现类DomainExtractingServerList#getUpdatedListOfServers方法

public class DomainExtractingServerList implements ServerList<DiscoveryEnabledServer> {@Overridepublic List<DiscoveryEnabledServer> getUpdatedListOfServers() {List<DiscoveryEnabledServer> servers = setZones(this.list.getUpdatedListOfServers());return servers;}

DomainExtractingServerList #getUpdatedListOfServers返回了一个DiscoveryEnabledServer对象的列表,它是通过发现获得的服务器,包含 InstanceInfo形式的元数据,继续跟踪下去,代码来DiscoveryEnabledNIWSServerList#getUpdatedListOfServers

public class DiscoveryEnabledNIWSServerList extends AbstractServerList<DiscoveryEnabledServer>{@Overridepublic List<DiscoveryEnabledServer> getUpdatedListOfServers(){return obtainServersViaDiscovery();}private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {List<DiscoveryEnabledServer> serverList = new ArrayList<DiscoveryEnabledServer>();if (eurekaClientProvider == null || eurekaClientProvider.get() == null) {logger.warn("EurekaClient has not been initialized yet, returning an empty list");return new ArrayList<DiscoveryEnabledServer>();}//Eureka客户端EurekaClient eurekaClient = eurekaClientProvider.get();if (vipAddresses!=null){for (String vipAddress : vipAddresses.split(",")) {// if targetRegion is null, it will be interpreted as the same region of client//【重要】从eurekaClient#getInstancesByVipAddress获取服务注册表List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, isSecure, targetRegion);for (InstanceInfo ii : listOfInstanceInfo) {if (ii.getStatus().equals(InstanceStatus.UP)) {if(shouldUseOverridePort){if(logger.isDebugEnabled()){logger.debug("Overriding port on client name: " + clientName + " to " + overridePort);}// copy is necessary since the InstanceInfo builder just uses the original reference,// and we don't want to corrupt the global eureka copy of the object which may be// used by other clients in our system//复制一个InstanceInfo ,为了不破坏原本的实例InstanceInfo copy = new InstanceInfo(ii);if(isSecure){ii = new InstanceInfo.Builder(copy).setSecurePort(overridePort).build();}else{ii = new InstanceInfo.Builder(copy).setPort(overridePort).build();}}//创建一个DiscoveryEnabledServer对象DiscoveryEnabledServer des = new DiscoveryEnabledServer(ii, isSecure, shouldUseIpAddr);des.setZone(DiscoveryClient.getZone(ii));//添加服务serverList.add(des);}}if (serverList.size()>0 && prioritizeVipAddressBasedServers){break; // if the current vipAddress has servers, we dont use subsequent vipAddress based servers}}}//返回服务列表return serverList;}

DomainExtractingServerList 是ServerList的实现类,它的作用是从Eureka客户端获取服务器信息的服务器列表类使用ServerList动态获取服务器列表 List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, isSecure, targetRegion);,并封装成DiscoveryEnabledServer 列表返回,继续跟踪,代码来到DiscoveryClient#getInstancesByVipAddress(java.lang.String, boolean, java.lang.String)


@Singleton
public class DiscoveryClient implements EurekaClient {...省略...@Overridepublic List<InstanceInfo> getInstancesByVipAddress(String vipAddress, boolean secure,@Nullable String region) {if (vipAddress == null) {throw new IllegalArgumentException("Supplied VIP Address cannot be null");}Applications applications;if (instanceRegionChecker.isLocalRegion(region)) {//【重要】获取本地缓存的服务注册表applications = this.localRegionApps.get();} else {applications = remoteRegionVsApps.get(region);if (null == applications) {logger.debug("No applications are defined for region {}, so returning an empty instance list for vip "+ "address {}.", region, vipAddress);return Collections.emptyList();}}if (!secure) {return applications.getInstancesByVirtualHostName(vipAddress);} else {return applications.getInstancesBySecureVirtualHostName(vipAddress);}}

这里看到,其实是从DiscoveryClient中localRegionApps本地缓存的服务注册表中获取服务,到这我们就搞清楚了Ribbon是如何去加载服务注册表作为负载均衡的候选服务列表的,获取到的服务注册表安装zone进行统计,最终是设置到LoadBalancerStats对象的Map<String, ZoneStats> zoneStatsMap中,然后交给BaseLoadBalancer维护起来,在Ribbon执行负载均衡的过程中会从负载均衡器取出服务列表作为候选的服务。

ServerListUpdater 更新负载均衡候选服务列表

上面有介绍到ServerListUpdater ,它是用来更新负载均衡的候选服务列表的,其中一个实现是PollingServerListUpdater,通过定时任务来更新服务列表

public class PollingServerListUpdater implements ServerListUpdater {//延迟时间private static long LISTOFSERVERS_CACHE_UPDATE_DELAY = 1000; // msecs;//定时任务间隔时间,更新服务列表private static int LISTOFSERVERS_CACHE_REPEAT_INTERVAL = 30 * 1000; // msecs;@Overridepublic synchronized void start(final UpdateAction updateAction) {if (isActive.compareAndSet(false, true)) {final Runnable wrapperRunnable = new Runnable() {@Overridepublic void run() {if (!isActive.get()) {if (scheduledFuture != null) {scheduledFuture.cancel(true);}return;}try {//更新动作,执行更新updateAction.doUpdate();lastUpdated = System.currentTimeMillis();} catch (Exception e) {logger.warn("Failed one update cycle", e);}}};//定时任务scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(wrapperRunnable,  //任务线程initialDelayMs,refreshIntervalMs,TimeUnit.MILLISECONDS    30s/次);} else {logger.info("Already active, no-op");}}

PollingServerListUpdater默认30s/次更新服务列表,定时任务执行的是updateAction.doUpdate(); 我们看一下这个方法

public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {...省略...protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {@Overridepublic void doUpdate() {//更新服务列表updateListOfServers();}};

真相大白,其实PollingServerListUpdater 最终也是通过DynamicServerListLoadBalancer#updateListOfServers去更新服务列表,只不过是定时更新的。

那么这个PollingServerListUpdater#Starter它是在哪儿被触发的呢?

在DynamicServerListLoadBalancer初始化方法restOfInit中有这么一行代码 enableAndInitLearnNewServersFeature(); 就是在开启服务列表的更新定时任务

public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {...省略...
//初始化void restOfInit(IClientConfig clientConfig) {boolean primeConnection = this.isEnablePrimingConnections();// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()this.setEnablePrimingConnections(false);//开启服务列表更新的定时任务enableAndInitLearnNewServersFeature();//服务列表更新的定时任务updateListOfServers();if (primeConnection && this.getPrimeConnections() != null) {this.getPrimeConnections().primeConnections(getReachableServers());}this.setEnablePrimingConnections(primeConnection);LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());}//调用 serverListUpdater.startpublic void enableAndInitLearnNewServersFeature() {LOGGER.info("Using serverListUpdater {}", serverListUpdater.getClass().getSimpleName());serverListUpdater.start(updateAction);}

在restOfInit方法中间接触发了PollingServerListUpdater #Start定时更新逻辑

所以这里总结一下,负载均衡器的候选服务列表其实就是从EurekaClient中的本地缓存的注册表中加载的,在DynamicServerListLoadBalancer构造器中初始化的时候被加载,也会通过定时任务更新,也可以通过Eureka事件通知的方式去更新。

总结


整理如下

  • RibbonAutoConfiguration 创建负载均衡客户端LoadBalancerClient
  • RibbonAutoConfiguration 创建SpringClientFactory初始化Ribbon上下文,注册相关的组件
  • LoadBalancerAutoConfiguration 定义好负载均衡拦截器LoadBalancerInterceptor,添加到RestTemplate
  • RibbonClientConfiguration 注册了ILoadBalancer,IRule,IPing,ServerList,ServerListFilter,ServerListUpdater
  • ILoadBalancer 的实现类DynamicServerListLoadBalancer 通过ServerList加载服务列表,通过ServerListFilter过滤,通过IPing检查可用性,通过ServerListUpdater定时更新服务列表

但是用RestTemplate使用服务名发起请求时会走如下流程

  • RestTemplate请求调用LoadBalancerInterceptor#intercept方法执行请求
  • 接着调用LoadBalancerClient#exec,服务名和LoadBalancerRequestFactory创建的LoadBalancerRequest请求对象作为参数
  • LoadBalancerClient#exec方法中调用ILoadBalancer#chooseServer选择一个服务
  • ILoadBalancer#chooseServer又调用IRule的负载均衡算法选择服务
  • 选择到服务后,调用LoadBalancerRequest#apply对选择的服务发起请求

当然这个执行步骤我们会在下一章去详细跟踪一下

文章有点长,这篇文章主要介绍了一下Ribbon初始化的一些组件,以及Ribbon的上下文创建流程,和Ribbon负载均衡器候选服务列表的加载流程,为下一篇文章Ribbon的执行流程打下基础

十四.SpringCloud源码剖析-Ribbon的初始化配置相关推荐

  1. SpringCloud源码:Ribbon负载均衡分析

    本文主要分析 SpringCloud 中 Ribbon 负载均衡流程和原理. SpringCloud版本为:Edgware.RELEASE. 一.时序图 和以前一样,先把图贴出来,直观一点: 二.源码 ...

  2. Dubbo(十四)源码解析 之 远程调用

    远程调用主要处理三个流程: 消费者向提供者发起请求 提供者处理消费者请求 消费者处理提供者响应 1. NettyClient 的创建 上一章服务订阅,有两个地方没有说完,其中之一:无论是本地注册表方式 ...

  3. Spark源码剖析 - SparkContext的初始化(八)_初始化管理器BlockManager

    8.初始化管理器BlockManager 无论是Spark的初始化阶段还是任务提交.执行阶段,始终离不开存储体系.Spark为了避免Hadoop读写磁盘的I/O操作成为性能瓶颈,优先将配置信息.计算结 ...

  4. STL源码剖析——空间配置器

    目录 构造和析构基本工具:construct() 和 destroy() 空间的配置与释放:std::alloc 二级空间配置器简述 空间配置函数allocate() 空间释放函数deallocate ...

  5. SpringBoot (一) 入门、配置、自动配置源码剖析理解

    文章目录 0 Spring Boot 1 Overview 1.1 Introduce **Spring** SpringBoot 微服务 1.2 快速上手 Hello World pom.xml s ...

  6. 【云原生微服务八】Ribbon负载均衡策略之WeightedResponseTimeRule源码剖析(响应时间加权)

    文章目录 一.前言 二.WeightedResponseTimeRule 1.计算权重? 1)如何更新权重? 2)如何计算权重? 3)例证权重的计算 2.权重的使用 1)权重区间问题? 一.前言 前置 ...

  7. Redis源码剖析和注释(十六)---- Redis输入输出的抽象(rio)

    Redis源码剖析和注释(十六)---- Redis输入输出的抽象(rio) . https://blog.csdn.net/men_wen/article/details/71131550 Redi ...

  8. redis源码剖析(十五)——客户端思维导图整理

    redis源码剖析(十五)--客户端执行逻辑结构整理 加载略慢

  9. GDAL源码剖析(四)之命令行程序说明二

    接博客GDAL源码剖析(四)之命令行程序说明一http://blog.csdn.net/liminlu0314/article/details/6978589 其中有个nearblack,gdalbu ...

最新文章

  1. Linux下使用Opencv打开笔记本摄像头
  2. spring 中读取properties 文件
  3. 人事信息管理系统(PMS)
  4. .NET中的内存管理
  5. a.pop啥意思python_python中pop什么意思
  6. springmvc为什么不能拦截jsp页面?
  7. VSCode USER GUIDE Basic Editing
  8. Linux多线程实践(四 )线程的特定数据
  9. leetcode957. Prison Cells After N Days
  10. 2016012013 王雪 散列函数的应用及其安全性
  11. redhat官网操作文档查找
  12. 2012 r2 万能网卡驱动_无线网卡怎么用,我来教您无线网卡怎么用的方法
  13. TensorFlow实现图像风格迁移
  14. 广告投放系统常用的几种防作弊方式
  15. 顺丰全栈资源下的自动化运维灵魂
  16. Cadence OrCAD Capture 自底而上的设计流程
  17. gamemaker studio socket例子
  18. 如何查看SQL Server的索引碎片情况并进行整理
  19. php session header,php session header()重定向后丢失 - php
  20. 利用xls下载链接下载资源

热门文章

  1. 大数据拓客AI智能营销有什么作用
  2. n!末尾有多少个0以及n!末尾第一个非0数字
  3. 认识HTTP以及迭代历程
  4. LeetCode 897. 递增顺序搜索树
  5. 计算机中常用的声音编辑工具有哪些,电脑上的音频编辑软件哪个最好用?
  6. 阿里云图像搜索服务--以图搜图,拍立淘功能实现
  7. 3.12黑天鹅:加密市场结构的崩溃
  8. 计算机硕士工资一览表 08年最新各大IT公司薪水行
  9. 图形图像概念以及在android中的应用
  10. ErrorResponse(code = AccessDenied, message = Access denied, bucketName = bucket, objectName = null