几个月前部门内容组织了一次系统设计的议题,分到我们头上的题目是设计一套灰度发布系统。嗯,然后我们就精心设计(参考公司现有系统)了一番,不过鉴于滴滴现在大部分的人都是百度来的(误,所以这种系统大概也都是差不多的思路实现而来的。所以感觉应该算是一种通用系统吧~

为什么要有灰度发布系统?灰度发布是现代互联网公司比较必然的一种需求,像滴滴这种规模的公司,千万级的司机和亿级的用户,如果不经小规模实际业务场景测试(代码本身的测试流程先不算)就对线上流程贸然修改、重构并且上线,必然会造成群体性事件,影响到公司的形象,伤害到终端用户,并最终波及到码农可怜的年终奖。而灰度发布能带来什么样的好处呢?可以让你少伤害一部分用户啊~如果出了问题,回滚或者对影响到的用户进行补偿都很方便(毕竟钱能解决的问题都不是问题,但规模太大的时候补偿手段就不好使了。

实际上在一个系统的灰度策略执行期间,老的逻辑和新的逻辑的代码在线上系统都是存在的。这样说的话可能有些人会提出异议,我们在做代码发布的时候先发布一台机器,然后再发布十台,然后一百台这样的,似乎是叫小流量上线,这个和灰度发布有区别么?

两者还是有一些区别的,小流量上线一般做的是系统的彻底升级,和灰度发布不一样。也即是前面提到的,灰度发布期间,线上的系统两套代码在同一台新发布的机器上也同时存在。实际上比较重要的业务系统都会做灰度发布,根据效果来决定之后是继续扩大灰度范围,还是进行回滚。而不是粗暴地进行升级。当然了,很多公司其实不是那么在乎这些事情。

灰度发布一般人都比较熟悉的案例可能是微软的操作系统升级吧,其实腾讯qq或者微信发布也差不多。不过说到操作系统升级,灰度发布感觉还有另外的一层意思。可以控制用户流量对网络或者系统的负载的影响。可以回想一下当初国内暴风影音的升级事件,导致联通整体网络瘫痪。当然了,扯远了。

继续来说互联网公司的灰度发布系统。

一般的灰度发布都会有一些策略,其实就是分类/桶策略。具体一些,比如有人说我想要新功能只对特定的几个用户开放,那我可以把用户分为两个类型:在白名单里的和不在白名单里的,这算是一种分类策略。再比如,我希望我能够以某种百分比或者千分比的形式进行上线,这也是一种分类策略,当然,这里可能叫分桶策略更合适。还有的人希望我能够以用户的gps定位城市进行分桶,或者以用户的手机号归属地进行分桶,再或者我想以移动联通电信进行分桶(其实还是有可能的哈哈)。

当然上面说的复杂了,其实大多数情况下只要有个百分比千分比就好了。

下面就用千分比来举例,简单设计一下这个灰度发布系统。

拿滴滴来举例吧,一般用户注册的时候都会有手机号信息和城市信息,所以手机号和城市都可以拿来做这个分桶的依据。假如我们要把用户分成1000个桶,我们不会直接拿手机号或者城市id来进行求模,而是会对这些信息做一个简单的单向哈希。

例如家喻户晓的md5或者sha1:

15810321343

md5=>  
05eadde36e5e5c3a00015a8f07d98d6b

sha1=>  
7962e1ba260de074ef895af44c62ad353ee36c2c  

一句题外话,迅雷的文件校验就是拿sha1做的,有时间可以另开一篇讲讲。

实际上这些单向哈希算法生成的这么长的字符串对于我们做个简单的求模来说并没有什么卵用。所以实际计算的时候只要随便取几位就行了。比如最后三位。

d6b转成10进制= 3435 mod 1000 = 435  

因此将这个用户放在第435个桶里。有了计算规则和输入内容。这个灰度系统其实也就简单成形了~给业务方提供的服务包括两部分,规则配置和api。规则部分由业务方指定,例如千分之七的灰度,那么我们就从第0个桶开始,一直到第七个桶,都是需要返回true的,而其它的用户则返回false。

听起来是不是很简单~麻雀虽小。

当然了,上面的这个方案还存在很多问题。

问题一:使用md5或者sha1的话,能保证分布均匀么?

这个不一定,选取实际系统中的手机号或者city_id简单进行计算和统计可以简单得到一些结果。如果分布不均匀的话怎么办呢?这时候可以通过给输入拼一个简单的字符串,然后再进行计算。这种方法在《分布式网站架构》书里有讲,“数字指纹”在内容发生小变化时会发生巨大变化。

问题二:从第0个桶开始选取是不是会有什么问题?

是的。。这样的话每次你上前千分之一都是固定的千分之一的用户。。为了解决这个问题,初始桶可以随机开始,但在规则配置好之后应该固化到规则中来。以免每次都是一样的用户受伤233

问题三:使用md5或者sha1是不是会有什么问题啊?

是的,虽然是单向哈希,但md5和sha1都是比较消耗cpu的操作,如果服务qps比较高的时候,会消耗大量的cpu资源。而对于我们来说,其实只是需要一个简单的哈希,但是消耗这么高的cpu是不是有点得不偿失?万幸的是,现在我们也有替代方案,即MurmurHash算法,下面是摘录的简介:

MurmurHash算法:高运算性能,低碰撞率,由Austin Appleby创建于2008年,现已应用到Hadoop、libstdc++、nginx、libmemcached等开源系统。2011年Appleby被Google雇佣,随后Google推出其变种的CityHash算法。  

所以计算过程可以用该算法做优化,具体的benchmark就留给读者去做吧。

问题四:只按百分比的灰度发布是不是太粗糙了。。

是的啊,虽然已经能够满足一定的需求。不过实际情况下肯定会需要有多种规则来进行灰度发布,所以这个系统总还是需要有一个简单的规则id,和对应的适配算法。当然了,即使为这个系统添加新的规则也不会太复杂~

解决了这些问题之后,我们提供给业务方的api是什么样的呢?

Request:

POST:  
{
    "key" : "15804323213",
    "rule_id" : "1"
}

Response:

{
    "status" : 0,
    "result" : true
}

很简单~