Ribbon 自动配置类
基于Spring Boot 提供的自动配置功能,Ribbon 自动配置的类在org.springframework.cloud.netflix.ribbon
包的RibbonClientConfiguration
和RibbonAutoConfiguration
配置类中。
RibbonAutoConfiguration
RibbonAutoConfiguration
相当于全局配置,主要是加载Ribbon 客户端工厂、配置类工厂、重试机制工厂等,这个配置类在启动的时候就会被加载。
首先看下RibbonAutoConfiguration
配置类上的注解:
// 标记为配置类Bean
@Configuration
// 注入Bean条件,RibbonClassesConditions类去判断是否注入该Bean
@Conditional({
RibbonAutoConfiguration.RibbonClassesConditions.class})
// @RibbonClients为所有的Ribbon客户端提供默认配置, @RibbonClients标记该类被注册为一个 RibbonClientSpecification
@RibbonClients
// 在加载配置的类之后再加载当前类
@AutoConfigureAfter(
name = {
"org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration"}
)
// 在加载配置的类之前再加载当前类
@AutoConfigureBefore({
LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
// 使RibbonEagerLoadProperties、ServerIntrospectorProperties配置类生效
@EnableConfigurationProperties({
RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
该类具有两个属性,使用@Autowired
注入
@Autowired(
required = false
)
// List集合,类型为RibbonClientSpecification,注入使用了@RibbonClients 的配置类。
private List<RibbonClientSpecification> configurations = new ArrayList();
// Ribbon的饥饿加载(eager-load)模式配置类,Ribbon进行客户端负载均衡的Client并不是在服务启动的时候就初
// 始化好的,而是在调用的时候才会去创建相应的Client,开启加载饥饿模式,可以提前创建。
@Autowired
private RibbonEagerLoadProperties ribbonEagerLoadProperties;
接下来看下这个配置类注入了那些Bean 对象:
// HasFeatures 可以查看系统启动的一些features,进而了解系统特征。
@Bean
public HasFeatures ribbonFeature() {
return HasFeatures.namedFeature("Ribbon", Ribbon.class);
}
// Spring 创建 Ribbon 客户端、负载均衡器、客户端配置实例的工厂
@Bean
@ConditionalOnMissingBean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
// LoadBalancerClient为springcloud提供的负载均衡器客户端,可以查询所有的服务并挑选一个
@Bean
@ConditionalOnMissingBean({
LoadBalancerClient.class})
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(this.springClientFactory());
}
// Ribbon重试工厂
@Bean
@ConditionalOnClass(
name = {
"org.springframework.retry.support.RetryTemplate"}
)
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(final SpringClientFactory clientFactory) {
return new RibbonLoadBalancedRetryFactory(clientFactory);
}
// 用来进行客户端配置类的获取,比如interface com.netflix.loadbalancer.IRule 规则配置类,对应一个属性=》NFLoadBalancerRuleClassName
@Bean
@ConditionalOnMissingBean
public PropertiesFactory propertiesFactory() {
return new PropertiesFactory();
}
// 如果配置了eager-load饥饿加载模式,就注册RibbonApplicationContextInitializer上下文初始化对象
@Bean
@ConditionalOnProperty({
"ribbon.eager-load.enabled"})
public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
return new RibbonApplicationContextInitializer(this.springClientFactory(), this.ribbonEagerLoadProperties.getClients());
}
RibbonClientConfiguration
RibbonClientConfiguration
则是Ribbon 客户端相关配置,这个类再执行第一次Feign 客户端请求的时候才会被加载。
比如当前Feign 客户端为order-service,它是第一次进行请求,那么会进入获取客户端配置方法:
然后在NamedContextFactory
中获取改客户端的上下文,没有的时候,会去创建。
protected AnnotationConfigApplicationContext getContext(String name) {
// 当前上下文是否包含了该客户端order-service
if (!this.contexts.containsKey(name)) {
synchronized(this.contexts) {
// 没有则创建并放入
if (!this.contexts.containsKey(name)) {
this.contexts.put(name, this.createContext(name));
}
}
}
// 有直接返回
return (AnnotationConfigApplicationContext)this.contexts.get(name);
}
最终调用NamedContextFactory
的createContext 方法,为每个客户端创建上下文。
通过NamedContextFactory
机制,按不同的应用创建了不同的子ApplicationContext,从而隔离不互相影响,每一个Feign 客户端配置不同的应用上下文,这样可以让每个不同的客户端使用不同的配置、不同的Bean。
在createContext 方法创建了客户端的上下文后,进入刷新,则会开始加载直接当前 Context 需要的Bean 、前置后置处理器等流程。RibbonClientConfiguration
就是在这个时候加载的。
// 在获取一个@FeignClient接口对应的代理对象前,先为其创建Context对象
protected AnnotationConfigApplicationContext createContext(String name) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 查询RibbonAutoConfiguration 中,使用了@RibbonClients 的配置类
if (this.configurations.containsKey(name)) {
// 配置类中有 名称为当前客户端的配置,则添加配置并注册到上下文中
Class[] var3 = ((NamedContextFactory.Specification)this.configurations.get(name)).getConfiguration();
int var4 = var3.length;
for(int var5 = 0; var5 < var4; ++var5) {
Class<?> configuration = var3[var5];
context.register(new Class[]{
configuration});
}
}
// 循环使用了@RibbonClients 的配置类
Iterator var9 = this.configurations.entrySet().iterator();
while(true) {
Entry entry;
do {
if (!var9.hasNext()) {
// 注册
context.register(new Class[]{
PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType});
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName, Collections.singletonMap(this.propertyName, name)));
if (this.parent != null) {
context.setParent(this.parent);
context.setClassLoader(this.parent.getClassLoader());
}
context.setDisplayName(this.generateDisplayName(name));
// 刷新上下文
context.refresh();
return context;
}
entry = (Entry)var9.next();
// 配置类不是以default. 开头,则进入do 循环,这里没有
} while(!((String)entry.getKey()).startsWith("default."));
Class[] var11 = ((NamedContextFactory.Specification)entry.getValue()).getConfiguration();
int var12 = var11.length;
// 循环默认配置,并注册
for(int var7 = 0; var7 < var12; ++var7) {
Class<?> configuration = var11[var7];
context.register(new Class[]{
configuration});
}
}
}
可以看到默认有两个@RibbonClients 的配置类:
创建上下文后,可以看到,当前客户端的上下文信息。
RibbonClientConfiguration
类使用@Import
导入了其他配置类。
@Import({
HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
RibbonClientConfiguration
类的属性如下:
// 默认的连接超时时间,默认1秒
public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
// 默认的读取超时时间,默认1秒
public static final int DEFAULT_READ_TIMEOUT = 1000;
public static final boolean DEFAULT_GZIP_PAYLOAD = true;
// @RibbonClientName Ribbon客户端名称
@RibbonClientName
private String name = "client";
// 配置工厂,由RibbonAutoConfiguration 注入
@Autowired
private PropertiesFactory propertiesFactory;
最后看下RibbonClientConfiguration
注入了哪些Bean :
// 当前客户端配置
@Bean
@ConditionalOnMissingBean
public IClientConfig ribbonClientConfig() {
// 默认的配置类
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
// 加载配置
config.loadProperties(this.name);
// 设置配置
config.set(CommonClientConfigKey.ConnectTimeout, 1000);
config.set(CommonClientConfigKey.ReadTimeout, 1000);
config.set(CommonClientConfigKey.GZipPayload, true);
return config;
}
// 加载负载均衡算法,默认ZoneAvoidanceRule 轮询。
@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
if (this.propertiesFactory.isSet(IRule.class, this.name)) {
return (IRule)this.propertiesFactory.get(IRule.class, config, this.name);
} else {
ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
rule.initWithNiwsConfig(config);
return rule;
}
}
// Ping 检测服务健康状态
@Bean
@ConditionalOnMissingBean
public IPing ribbonPing(IClientConfig config) {
return (IPing)(this.propertiesFactory.isSet(IPing.class, this.name) ? (IPing)this.propertiesFactory.get(IPing.class, config, this.name) : new DummyPing());
}
// Ribbon的服务列表
@Bean
@ConditionalOnMissingBean
public ServerList<Server> ribbonServerList(IClientConfig config) {
if (this.propertiesFactory.isSet(ServerList.class, this.name)) {
return (ServerList)this.propertiesFactory.get(ServerList.class, config, this.name);
} else {
ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
serverList.initWithNiwsConfig(config);
return serverList;
}
}
// 服务列表更新器
@Bean
@ConditionalOnMissingBean
public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
return new PollingServerListUpdater(config);
}
// 负载均衡器ILoadBalancer
@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerList<Server> serverList, ServerListFilter<Server> serverListFilter, IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
return (ILoadBalancer)(this.propertiesFactory.isSet(ILoadBalancer.class, this.name) ? (ILoadBalancer)this.propertiesFactory.get(ILoadBalancer.class, config, this.name) : new ZoneAwareLoadBalancer(config, rule, ping, serverList, serverListFilter, serverListUpdater));
}
// Ribbon的LoadBalancer五大组件之ServerListFilter服务列表过滤器
@Bean
@ConditionalOnMissingBean
public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
if (this.propertiesFactory.isSet(ServerListFilter.class, this.name)) {
return (ServerListFilter)this.propertiesFactory.get(ServerListFilter.class, config, this.name);
} else {
ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
filter.initWithNiwsConfig(config);
return filter;
}
}
// 负载均衡器上下文
@Bean
@ConditionalOnMissingBean
public RibbonLoadBalancerContext ribbonLoadBalancerContext(ILoadBalancer loadBalancer, IClientConfig config, RetryHandler retryHandler) {
return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler);
}
// 重试处理器
@Bean
@ConditionalOnMissingBean
public RetryHandler retryHandler(IClientConfig config) {
return new DefaultLoadBalancerRetryHandler(config);
}
// 服务拦截器
@Bean
@ConditionalOnMissingBean
public ServerIntrospector serverIntrospector() {
return new DefaultServerIntrospector();
}
// 后置处理器
@PostConstruct
public void preprocess() {
RibbonUtils.setRibbonProperty(this.name, CommonClientConfigKey.DeploymentContextBasedVipAddresses.key(), this.name);
}
总结
项目启动=》RibbonAutoConfiguration(加载工厂类,默认配置)=》第一次访问=》代理客户端=》初始化该客户端上下文=》RibbonClientConfiguration(加载服务列表、过滤器、拦截器、客户端配置等)=》处理请求。