+-
spring cloud gateway+ribbon 组合指定版本权重分流(简易灰度发布实现)

0.参考

文章1 ribbon中使用HystrixRequestVariableDefault的一个注意事项

文章2 SpringCloud灰度发布实践(附源码) zuul实现的可以参考。

1.思路

改造gataway的### Weight Route Predicate Factory,在指定权重的同时指定每个对应权重的服务的版本号。主要需要改写的地方是要在分流之后,将版本号传给ribbon,ribbon在做负载均衡选择的时候,根据传入的版本号,与服务的eureka.matedata中的version匹配,从而达到灰度发布的目的。
gateway的配置类似如下,VersionWeight为重写的断言工厂。

    - id: temp_old
      uri: lb://TEMPLATE
      predicates:
        - Path=/temp/**
        - VersionWeight=group1, 10, v1
      filters:
        - StripPrefix=1
    - id: temp_new
      uri: lb://TEMPLATE
      predicates:
        - Path=/temp/**
        - VersionWeight=group1, 1, v2
      filters:
        - StripPrefix=1

2.调查改造点

debug后知道了整个请求顺序

客户端-> gateway filter -> LoadBalancerClientFilter -> (如果使用ribbon) RibbonLoadBalancerClient-> (某个Rule) XXXAvoidanceRule -> (对应的 Predicate) XXXAvoidancePredicate

所以选择的服务的关键就是Ribbon的rule和Predicate

步骤

写一个grayContext, 基于threadlocal处理version的上下文传递。 自定义负载策略:负载策略的关键是Rule 和 Predicate, Rule 继承 PredicateBasedRule, Predicate 继承 AbstractServerPredicate, 实现Predicate 中的 apply方法 此方法是负载策略核心(逻辑:从grayContext获取version,与服务的eureka.matedata中的version匹配。匹配上则为目标服务) 改写权重断言策略:改写WeightRoutePredicateFactory,在Factory 分流完成后,把路由中配置的 version 放入到grayContext 中。并且要将vesion 放入到 request的head中,用来向下传递。

上面是对gateway的改造,经过改造后,gateway分发的时候就根据version能指定请求的服务器,接下来是微服务间通过fegin或者restTemplate调用时的改造,目的是将version继续往下传递

对request请求拦截:在微服务中对request请求进行拦截,将request中的version放到grayContext中 对fegin添加拦截器:实现RequestInterceptor,从grayContext中取出version放在头部中。fegin的loadbanced也是基于ribbon的,所以使用自定义负载策略进行分发,策略和gateway一致。 如果使用restTemplate,添加拦截器:实现ClientHttpRequestInterceptor,从grayContext中取出version放在头部中。

这样就将version串联起来,从而实现了指定流量到对应的服务。

注意点

如果断路器使用Hystrix,要注意Hystrix用信号量隔离,没问题,如果使用线程池隔离,我们基于threadlocal处理version就无效了,
hystrix有提供对应的方法 使用 HystrixRequestVariableDefault
参考文章1这篇文章中提到的使用方式,能理解HystrixRequestVariableDefault是个怎样的用法,其作用和threadlocal类似。

3.待解决问题

不使用ribbon的情况,引入spring-cloud-loadbalancer, loadbalancer 和 ribbon差蛮多,不支持自动刷新serverlist,只有一个RoundRobinLoadBalancer。 会使用ReactiveLoadBalancerClientFilter,要通过实现ReactorServiceInstanceLoadBalancer 接口 来实现负载策略
感觉没太大差,但是要自己实现自动刷新serverlist。可以参考ribbon的做法来做。 推荐是使用spring-cloud-loadbalancer,因为ribbon是阻塞的。 官网推荐使用CircuitBreaker, 因为Hystrix 不维护了。CircuitBreaker 只有信号量隔离,可以直接使用threadlocal。