【SpringCloud】Ribbon如何自定义客户端配置和全局配置

事情的起因是这样的,公司内部要实现基于Zuul网关的灰度路由,在上线时进行灰度测试,故需要配置业务微服务向Eureka注册的metadata元数据,和自定义Ribbon的负载规则达到只访问灰度服务的目的。这样就需要自定义Ribbon的IRule,实现灰度请求只会负载到带有灰度标签元数据的业务微服务上,当自定义IRule规则开发好后,问题是如何将这个IRule规则配置给某个Ribbon Client或者全局生效。

本次使用Spring Cloud Dalston.SR5版本

在其 官方文档 中其实已经给出了一些如何针对某个Client 或者 修改默认配置的方式,但没有说明为什么这样使用

下面将按照这样的思路分析:

简单分析Spring Cloud Ribbon启动时如何自动配置的,以了解其装配到Spring中的Bean

Spring Cloud Ribbon Client的懒加载

Spring Cloud Ribbon Client的配置加载,包含全局配置及Client配置

如何自定义Client配置、全局配置

解释官方文档中的一些注意事项


Spring Cloud Ribbon自动配置

当前版本中的Netflix所有自动配置都在spring-cloud-netflix-core-xxx.jar中,根据其META-INF/spring.factories中的配置得知,Spring Cloud Ribbon的自动配置类为 RibbonAutoConfiguration

【SpringCloud】Ribbon如何自定义客户端配置和全局配置


RibbonAutoConfiguration @Configuration @ConditionalOnClass({ IClient.class, RestTemplate.class, AsyncRestTemplate.class, Ribbon.class}) @RibbonClients @AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration") @AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class}) @EnableConfigurationProperties(RibbonEagerLoadProperties.class) public class RibbonAutoConfiguration { // 所有针对某个RibbonClient指定的配置 @Autowired(required = false) private List<RibbonClientSpecification> configurations = new ArrayList<>(); // ribbon是否懒加载的配置文件 @Autowired private RibbonEagerLoadProperties ribbonEagerLoadProperties; // Spring会给每个RibbonClient创建独立的ApplicationContext上下文 // 并在其上下文中创建RibbonClient对应的Bean:如IClient、ILoadbalancer等 @Bean public SpringClientFactory springClientFactory() { SpringClientFactory factory = new SpringClientFactory(); factory.setConfigurations(this.configurations); return factory; } // Spring创建的带负载均衡功能的Client,会使用SpringClientFactory创建对应的Bean和配置 @Bean @ConditionalOnMissingBean(LoadBalancerClient.class) public LoadBalancerClient loadBalancerClient() { return new RibbonLoadBalancerClient(springClientFactory()); } // 到Spring environment中加载针对某个Client的Ribbon的核心接口实现类 @Bean @ConditionalOnMissingBean public PropertiesFactory propertiesFactory() { return new PropertiesFactory(); } // 如果不是懒加载,启动时就使用RibbonApplicationContextInitializer加载并初始化客户端配置 @Bean @ConditionalOnProperty(value = "ribbon.eager-load.enabled", matchIfMissing = false) public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() { return new RibbonApplicationContextInitializer(springClientFactory(), ribbonEagerLoadProperties.getClients()); } ...... }

上面RibbonAutoConfiguration创建的Bean主要分以下几类:

为Ribbon Client创建环境及获取配置

SpringClientFactory: 会给每个Ribbon Client创建一个独立的Spring应用上下文ApplicationContext,并在其中加载对应的配置及Ribbon核心接口的实现类

PropertiesFactory: 用于从Spring enviroment环境中获取针对某个Ribbon Client配置的核心接口实现类,并实例化

创建RibbonLoadBalancerClient,并将springClientFactory注入,方便从中获取对应的配置及实现类,RibbonLoadBalancerClient是Spring对LoadBalancerClient接口的实现类,其execute()方法提供客户端负载均衡能力

懒加载相关

RibbonEagerLoadProperties: 懒加载配置项Properties,可以指定是否懒加载,及哪些Client不懒加载

RibbonApplicationContextInitializer: 启动时就加载RibbonClient配置(非懒加载)的初始化器

可以看到默认启动流程中并没有加载RibbonClient的上下文和配置信息,而是在使用时才加载,即懒加载


Spring Cloud RibbonClient的懒加载

既然是在使用时才会加载,那么以Zuul网关为例,在其RibbonRoutingFilter中会创建RibbonCommand,其包含了Ribbon的负载均衡

//## RibbonRoutingFilter Zuul负责路由的Filter public class RibbonRoutingFilter extends ZuulFilter { @Override public Object run() { RequestContext context = RequestContext.getCurrentContext(); this.helper.addIgnoredHeaders(); try { RibbonCommandContext commandContext = buildCommandContext(context); ClientHttpResponse response = forward(commandContext); setResponse(response); return response; } catch (ZuulException ex) { throw new ZuulRuntimeException(ex); } catch (Exception ex) { throw new ZuulRuntimeException(ex); } } protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception { Map<String, Object> info = this.helper.debug(context.getMethod(), context.getUri(), context.getHeaders(), context.getParams(), context.getRequestEntity()); // 使用ribbonCommandFactory创建RibbonCommand RibbonCommand command = this.ribbonCommandFactory.create(context); try { ClientHttpResponse response = command.execute(); this.helper.appendDebug(info, response.getStatusCode().value(), response.getHeaders()); return response; } catch (HystrixRuntimeException ex) { return handleException(info, ex); } } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwfggd.html