事情的起因是这样的,公司内部要实现基于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配置、全局配置
解释官方文档中的一些注意事项
当前版本中的Netflix所有自动配置都在spring-cloud-netflix-core-xxx.jar中,根据其META-INF/spring.factories中的配置得知,Spring Cloud Ribbon的自动配置类为 RibbonAutoConfiguration
上面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的上下文和配置信息,而是在使用时才加载,即懒加载
既然是在使用时才会加载,那么以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); } } }