背景
目前我公司自己搭了一套发布系统,底层使用的是docker的swarm,实现了“灰度发布”功能。当开启发布灰度发布的时候,会独立启动一个容器,只允许指定百分比的流量进入到灰度容器中。
这一套机制,是依靠在网关中对后端http服务实例做不同的权限实现的,只能对http服务生效。
dubbo服务是直接跟注册中心zookeeper拿到所有服务提供者的地址,然后直连服务提供者,并不会经过网关。
dubbo负载均衡
dubbo的负载均衡机制,其实比我们想的要强大一点,以RandomLoadBalance随机负载均衡为例:
随机的负载均衡,并不是简单的有3个实例,随机从3个实例中选择1个,每个实例被选中的概率一致。
而是有权重的,默认权重是100,如果你给一个实例设置权重为200,那么被选中的概率就会是其他实例的2倍。
如何改变服务权重
dubbo-admin是可以为服务配置更重规则:路由、权重、访问权限…
下面截图是dubbo-admin配置权重规则的界面,通过增加规则,可以改变指定id的服务的权重:
configurators机制
我们在dubbo-admin添加权重规则,dubbo-admin底层是做了什么事情呢?
其实就是往“/dubbo/接口名/configurators/”目录下增加一个配置节点。
例如我们给一个运行在127.0.0.1的20880端口的com.alibaba.dubbo.demo.DemoService服务增加一个权重为400的规则,那内容会是这样:
override://127.0.0.1:20880/com.alibaba.dubbo.demo.DemoService
?category=configurators&dynamic=false&enabled=true&weight=400 的UrlEncode后的内容。
- 协议override,表示采用覆盖方式。即configurator的url上的参数会覆盖provider上的
- 127.0.0.1:20880,表示这个规则,仅仅对ip为127.0.0.1端口为20880的服务生效
- com.alibaba.dubbo.demo.DemoService,表示只对指定的DemoService服务生效
- dynamic=false, 表示该 URL 为持久数据,即使注册该 URL 的节点退出,该 URL 依旧会保存在注册中心。
- enabled=true,表示该 URL 的覆盖规则可用,consumer会过滤掉enabled=false的规则。
- weight=400,表示ip、端口、接口都匹配的provider url中的weight参数会被覆盖成weight=400
消费者在完成初始化之后会订阅 providers、configurators 和 routers 三个目录。相关的代码在RegistryProtocol中:
在这三个目录下发生变化的时候,就会触发 RegistryDirectory 的 notify() 方法。configurators 类型的 URL会 转化为 Configurator,保存到 configurators 字段中。
在后面的refreshOverrideAndInvoker方法,会将url转换成invoker对象,里面调用了 mergeUrl() 方法对 URL 参数进行合并。mergeUrl() 会将注册中心中 configurators 目录下的 URL(override 协议),以及服务治理控制台动态添加的配置(dubbo2.7)与 provider的URL进行合并,即覆盖 provider的URL 原有的一些信息。
然后会调用Configurator的configure方法,通过enabled参数和host等条件判断是否执行覆盖操作。
移除不能动态修改的数据后,直接调用addParameters方法进行覆盖。
灰度方案
要实现跟http服务一样的灰度发布机制,就可以利用权重规则来实现:
在业务服务启动的时候,也往zookeeper注册一个自己地址和端口的configurators子节点,将自己服务的权重改变。例如当前有2个实例,希望灰度发布,灰度容器只处理25%的流量,只需要在服务对外提供服务之前增加一个权重为67的动态规则。必须是动态的,这样当灰度容器停了,规则才会被自动删除了,不会对后续的服务发布产生影响。