灰度发布系统的实现

灰度发布,已经不是一个很新的概念了.一个产品,如果需要快速迭代开发上线,又要保证质量,保证刚上线的系统,一旦出现问题那么可以很快的控制影响面,就需要设计一套灰度发布系统.

灰度发布系统的作用在于,可以根据自己的配置,来将用户的流量导到新上线的系统上,来快速验证新的功能修改,而一旦出问题,也可以马上的恢复,简单的说,就是一套A/BTest系统.

它大抵的架构,应该是类似这样的:

其中分为几个部分:

  1. 接入层,接入客户端请求,根据下发的配置将符合条件的请求转发到新旧系统上.
  2. 配置管理后台,这个后台可以配置不同的转发策略给接入层.
  3. 新旧两种处理客户端请求的业务服务器.

关于接入策略的设计上,从协议层来说,需要从一开始就设计是根据哪些参数来进行转发的,而且这些参数最好跟具体的协议体内容分开,这样减少接入层对协议的解析.举个例子,如果客户端的请求是走HTTP协议的,那么将这些参数放在HEADER部分就好了,接入层不需要去具体解析body部分的数据就拿到了转发策略需要的参数.当然,放在HEADER中的数据,因为没有了加密性,又是需要考虑的另一个问题.

当然,最简单粗暴的转发策略,可以根据客户端ip地址来做,这是比较粗略的一个划分策略.

同样的,新旧服务器要对新旧客户端的协议兼容,也是能做到灰度发布的根本,如何设计一个扩展性好的应用协议,这一点就不在这里考虑了.

接下来,还需要满足如果管理后台下发了新的转发策略,接入层应该是可以马上感知到然后切换到这个新的策略来的.有好些不同的做法.假如接入层是Nginx这样的服务器,使用者只是在上面写了自己的Nginx模块来实现策略的转发,那么可能还需要在每台接入服务器上部署一个Agent的服务,主要用于:

  1. 接收管理后台下发的策略,更新Nginx配置,然后优雅重启Nginx服务.
  2. 定时检查本台机器的Nginx服务的状态,进行上报.

如果接入层不是Nginx这样的服务,那么也可以做一个pub-sub模型的订阅者,用ZK或者Redis都可以,订阅管理后台下发的服务进行处理即可.



上周写完灰度发布系统相关的博文之后,有朋友表示灰度系统的实现太过简单了,因为我目前接触的系统确实比较简单,很多复杂的东西没有考虑周全,如果更大型的业务系统,涉及到的服务更多,还有如果掺杂着数据的迁移,就更复杂了.这里就把当时讨论的内容提取出来,主要的贡献者为滴滴的沈佳伟.

1.调用链上有多个业务服务的场景

考虑这样一个业务场景,假设对外提供了服务A给客户端访问,服务A后面会调用服务B,C,D,此时需要上线一个功能,这个功能涉及到了服务A,C的修改,但是服务B,D不需要变动,换言之,我们的意图是,如果一个客户端请求,走到了新的灰度服务A,那么最终这个请求也应该走到这次和A一起灰度的服务C上.

这里的处理策略,可以给客户端请求进行tag打标记的方式,比如经由新版本服务A处理的请求,全部打上tag A,而在服务C上,也有接入层进行转发,它转发的策略之一就是根据根据这个tag来进行转发,这个系统如下图所示:

上图中,请求首先走到了旧版本的服务A上,该服务没有对请求打上tag,所以后续访问的都是没有配套灰度的旧版本C服务.

上图中,请求首先走到了新版本需要灰度的服务A上,在经过该服务处理后,给请求打上了tag A,由于带上了tag,后续访问的都是配套灰度的C服务.

简单的总结下,涉及到一个调用链路上某几个服务需要灰度的情况,可以通过tag的方式,将走灰度服务的请求汇集到一起来,如果一个请求走到了一个灰度路径上,就打上一个tag,这样只有有这个tag的请求才能走到这条链路上后续也需要一起灰度的服务上.至于如何给请求打tag,如果是HTTP协议,那就很简单了,也是加Header的方式,否则需要在设计协议的时候就考虑协议的扩展性支持这个操作.

2. 涉及到数据的灰度服务

假设灰度的服务,需要使用到数据库,如果灰度前后数据库的字段保持不变,那么新旧两套系统使用同一套数据库就可以了.

如果前后数据不一致,需要处理的情况就比较复杂,分为以下几种情况.

  • 部分灰度

在部分灰度的情况下,有部分请求到旧系统上,另一部分请求到了新的灰度系统上.走到旧系统的请求,还是照原样处理.但是走到了新版灰度系统的请求,需要同时将请求转发给旧系统上来对应的接口上修改旧系统的数据.如果走到新系统的请求查不到该用户的数据,还需要首先同步一份来新系统上.如果是事务性的请求,以写入老系统成功来做为操作成功的标准.

  • 全部灰度

在灰度系统已经全部接管了线上流量之后,为了安全起见,仍然需要对新老系统进行双写,步骤和前面一样.

  • 灰度完成

灰度完成与前面的全量灰度状态不太一样,区别在于前面的全量灰度状态下,仍然不能肯定系统一定是没有问题的,所以需要进行新旧系统的双写来保证数据可以在老系统上进行回滚.而在灰度完成状态,此时认为这个新版本已经完全通过了验证,无需再写入旧系统了.但是此时可能存在部分在灰度期间没有上线的用户,此时需要做一次同步,从旧系统上将这部分数据同步过来.

可以看到,这三个状态下,对新旧系统是否进行双写,做了严格的区分,目的只有一个:一旦新上线的系统出现问题,可以马上撤掉灰度系统,而这期间用户的任何修改在旧系统上都是可以找到的.


你可能感兴趣的:(设计与架构)