白玉试毒 | 灰度架构设计方案

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#006400","name":"user"}}],"text":"前言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"灰度发布并非是近几年才兴起的概念,诞生有一定的年头了,但至今,"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000080","name":"user"}}],"text":"绝大多数中小型互联网企业的发布流程中仍然缺少对灰度环境的支持,其主要原因在于大家对灰度的认知及成本等方面的综合考虑"},{"type":"text","text":"。我前段时间曾就职于一家初创型互联网企业,就发布流程而言,用“脏乱差”来形容似乎并不为过,并且,大部分人对环境的认知也仅限于开发(DEV)、测试(FAT),以及生产(PRO),从严谨性和专业性等2个角度来看,这并不合理。互联网企业的一大特点就是服务的功能变动异常频繁,自然发布的节奏也随之变得急促起来,缺少预发布环境和灰度环境的支撑,高频的服务发布往往会伴随着巨大的风险,以及那令人极度失望的服务质量。因此,本文将重点讨论一种平滑的服务发布方式,以便于帮助企业有效降低风险的影响范围。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#006400","name":"user"}}],"text":"何为灰度发布"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"据史书记载,在中国古代的东汉末年三国时期,曹操为了弥补军饷的不足,专门设立了发丘中郎将,摸金校尉等军衔,专司盗墓取财,贴补军饷之用,由此开启了倒斗的先例。据隋代医家巢元方撰写的《诸病源候论》一书记载“入井冢墓毒瓦斯候”,因此后来的盗墓者们在每次下墓前,都会先将几只金丝雀(又名:白玉)放至鸟笼中,然后将鸟笼系上绳子后投放至墓中,如图1所示。由于金丝雀具备特殊的呼吸系统结构,使得它们天生就对空气中含有的有害气体十分敏感,因此,当金丝雀被投放至墓中因为晕倒而不再鸣叫时,则表示墓中含有较高浓度的有害气体,不宜下墓,反之,就表示相对安全,这就是灰度发布(也被称之为金丝雀发布)的起源。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5a/5ae75b80343162a1ef659d303dee720e.png","alt":null,"title":"图1 白玉试毒","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"灰度发布,简单来说,就是通过定制一系列的规则和策略,先将一小部分的用户作为金丝雀,让其请求路由到新服务上进行观察,待运行正常后,再逐步导流更多的用户到灰度环境中;当然,如果初期灰度环境的机器数量不多,为了避免系统容量被持续递增的用户流量撑爆而产生宕机,我们还需要实时扩容灰度环境的机器数量以便于支撑更多的用户访问。当所有的用户都顺利切换到新服务上后,再停机之前的老版本服务,即可完成灰度发布。在此大家需要注意,就算灰度期新版本服务存在问题,我们也能够迅速将流量切换回来。总之,"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000080","name":"user"}}],"text":"实施灰度发布的目的就在于试错,尽可能控制和缩小问题的影响范围,从而保障绝大多数用户可用"},{"type":"text","text":";换个角度看,产品经理们自然也希望能够在最低的试错成本下收集用户的使用反馈(ABtest,灰度发布的其中一种表现形式),以便于更好的完善和改进当前产品。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#006400","name":"user"}}],"text":"灰度策略设计"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最常见的灰度策略就是指定白名单,只有在白名单中的那部分用户才允许被路由到灰度环境中。通常,这类用户要么是内测用户,要么是企业内部成员。除此之外,对于一个成熟的灰度产品而言,灰度策略的支持绝不应该如此单薄,比如像:访问权重比例、IP段,甚至是用户画像(根据目标用户群体的喜好、年纪、性别等一系列用户特征来进行灰度用户筛选)等策略也理应支持。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大家思考下,假设我们仅希望全站10%的用户访问灰度环境,访问权重的算法应该如何设计?最简单的做法就是提前load出所有用户然后挑选出其中10%的用户比例,尽管可以这样做,但我并不建议大家采用这样的方式,首先是灵活性的问题,其次,当用户基数较大时,这样的筛选方式无疑是低效的,况且用户数量每天都在发生变化。这里我为大家提供一种相对灵活且非常简单的权重算法,如下所示:"}]},{"type":"katexblock","attrs":{"mathString":"UID mod100"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假设UID是相对有序的,当得出余数后,再与指定的比例值进行比较(这里的比例值为10%),如果"},{"type":"codeinline","content":[{"type":"text","text":"UID % 100 <= 10"}]},{"type":"text","text":",那么我们就可以将目标用户指定为一个灰度用户,并将其路由到灰度环境中。采用求余算法实现动态灵活的权重控制尽管非常简单,但仍然存在一定的不足,比如,当全站用户基数相对较小时,其误差会变得较大;其次,在做加法时(比如权重比例从10%上调至20%),必然会导致某些固定用户每次都被命中,因此我们往往需要添加计算因子来避免这样的情况出现。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#006400","name":"user"}}],"text":"流量的切分"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000080","name":"user"}}],"text":"灰度发布的核心技术问题就是流量应该如何切分,以及在哪里进行切分"},{"type":"text","text":"。实际上灰度逻辑与业务逻辑本身是无关的,我们并不应该将灰度逻辑耦合在业务代码中,因此,将接入层网关作为流量的切分入口再适合不过,如图1所示:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/80/80d0b2ead88b1e079a24506d618a30c1.png","alt":null,"title":"图1 基于网关实现流量切分","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"灰度规则的配置,我们需要单独引入一个规则配置中心,并通过Redis的Pub/Sub模式将灰度规则进行下发,由网关层负责加载前者下发的规则内容。当网关接收到用户请求后,首先会从Header中解析出相应的灰度标记,如果当前用户为灰度用户,就将流量切分到灰度环境,反之切分到生产环境。在此大家需要注意,客户端请求头中所带的灰度标记,需要从规则配置中心处获取,也就是说,当客户端启动时,优先请求规则配置中心,如果命中规则,则每次请求网关时都会带上灰度标记,如图2所示:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/78/78bf61755ef4617984d82ce991651802.png","alt":null,"title":"图2 灰度流程1","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大家思考下,假设用户A为灰度用户,待我们实时调整灰度策略后前者已则不再是灰度用户时,应该如何让其路由到正确的环境中?也就是说,我们需要引入一种策略,能够让网关层有效适应规则的变化。"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#000080","name":"user"}}],"text":"我们可以为每一份灰度规则都指定一个自增的唯一版本号"},{"type":"text","text":",当规则发生改变时,版本号递增,这样一来,当灰度用户使用低版本进行请求时,允许它此次请求继续路由到灰度环境,待Response时将更新通知设置在响应头中,以便于客户端感知到规则变化后重新请求规则配置中心,如图3所示:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ce/ce2fd3c5f56e9760ec96a5b701d604d8.png","alt":null,"title":"图3 灰度流程2","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#006400","name":"user"}}],"text":"前端灰度"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#000080","name":"user"}}],"text":"除了后端服务能够实现灰度发布外,前端(这里泛指整个大前端,比如:IOS、Android、H5等)代码也允许进行灰度发布"},{"type":"text","text":"。从我之前参与的实施过程来看,这似乎更像是ABtest的一种体现。刚才提及过,实施灰度发布的目的就在于试错,从产品经理的角度来看,能够以最低的成本收集用户的使用反馈(比如某些功能突然改变了用户的使用习惯)自然是再好不过的。前端灰度的整个实施过程与后端灰度大致相同,如图4所示:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/16/16f7c158bf26e167f38f6708d55f5ac8.png","alt":null,"title":"图4 客户端灰度","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以理解为,前端灰度的本质实质上就是根据灰度标记来为不同的用户展示不同的功能界面(即:前端代码)。在此大家需要注意,如果在版本管控不好的情况下,前后端一起灰度时所带来的后果将会是致命的。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#006400","name":"user"}}],"text":"发布流程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们之前的整个灰度发布流程并没有像之前设计时预想得那么美好,因为自身业务原因,遇到了一些无解的问题,因此我们的灰度发布流程,并不是像之前介绍时所描述的那样(切换A/B2套环境),仅仅只是切分很小一部分流量到灰度环境中(灰度环境也只是部署一个小规模的灰度服务集群),如果按照访问权重比例换算的话,大约占全站的10%左右。当灰度验证结束后,我们再将生产环境中的所有服务替换为新服务,并关闭灰度开关,下线灰度集群,将用户流量100%切分到生产环境中,如图5所示:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d8/d861d23458e0fdb0de2dc72a9d5f499f.png","alt":null,"title":"图5 发布流程","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于我们仅仅只是切分了很小一部分用户流量至灰度环境,因此哪怕是灰度环境服务存在问题,我们也能够做到快速回滚,最大程度上控制和缩小问题的影响范围。在此大家需要注意,解决发布问题的方式有很多,除了本文所阐述的灰度发布外,市面上还流行有蓝绿发布、滚动发布等,因此,我没有办法告诉你哪一种是最优解决方案,具体还需要结合自身的业务场景而定。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至此,本文内容全部结束。如果在阅读过程中有任何疑问,欢迎加入微信群聊和小伙伴们一起参与讨论。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d4/d459ac18cf3bbee0634e195f7fd588a2.png","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"boxShadow"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#006400","name":"user"}}],"text":"码字不易,欢迎转发"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章