知易行难,双活过程中遇到了非常多的问题,但是回过头看很难完美的表述出来,之所以这么久才行文也是这个原因,总是希望可以尽可能的复现当时的思考、问题细节及解决方案,但是写出来才发现能给出的都是多次打磨、摸索之后的我们认为偏合理的方案;不过换个角度看,给大家展示出来一个正确答案,是否有更积极的参考价值呢? 以及,涉及到容器、发布平台、底层网络运维、监控等组件的内容,限于视野及技术能力并未包含在内,仅聚焦在业务团队及中间件组件的设计及改造上。
2022年,基于对稳定性的焦虑...和思考,交易平台联动中间件平台启动过异地多活项目的探索,虽然完成了核心应用及基础组件的改造,但在疫情&降本增效的影响下并未真正投产,同时也缺乏充分的测试以及线上流量的大规模验证;后续在不断的业务迭代中,相关设计及代码被冲击的面目全非,相关的多活自动化测试case也并没有沉淀下来。
随着近期外部友商时有严重故障出现,比如
以上林林总总出现的故障都给我们敲响了警钟,必须建设快速恢复的能力。出现问题几乎不可避免,但如果能控制影响范围、缩短影响时间,也就能把损失降到最低。
我经历过的公司,做交易的和做中间件的往往是最容易焦虑也最容易心态失衡的两拨技术人;一方面所有问题都会暴露在C端用户面前,影响范围大且不像toB/toM的场景 避开高峰期甚至有可能无人知晓;另一方面流量高,压力大,容易面临突发流量及突发事件,稳定性这根弦需要始终绷紧;所以往往是面向稳定性(的焦虑)设计,当然熬过去成长也最快。
回到我们的现状,得物目前的交易应用及中间件基础组件都是基于某云部署,且前期为了降低跨机房调用产生的网络损耗,较多应用都绑定了存储组件(db/redis/hbase)及核心依赖下游的所在可用区,对此,为了避免在极端情况下,得物的交易主链路出现长时间不可用的情况,团队决定提前预防,启动同城双活项目。
为了避免在极端情况下,得物的交易主链路出现长时间不可用的情况,团队决定启动同城双活项目,目标是快速建设流量动态切换能力及快速恢复能力,同时降低改造难度、减少改造工作量,不增加大量额外成本。团队讨论决策绕过之前最复杂也最容易出问题的数据同步(db双向同步、redis双向同步等),同时也不需要在流量切换时做db禁写,整体具有比较大的可操作可实施性。
多说一句,同城双活也有做数据双向同步的case,当然更彻底--每个机房都有全量的数据及应用,某个机房出问题 可以完全自闭环承接流量,不过带来的复杂度上升、成本上升也会比较明显,所以这次并没有选择这条路。换句话说,个人更倾向于小成本低风险快速落地,实现从0到1的功能建设,而不是大而全的方案,万一期间遇到问题只能徒呼奈何。当然在现阶段,通过建设相对低风险低投入的同城双活,积累更多基础能力的同时锻炼团队,选择最合适当下的方案,解决目前排在第一位的问题,怎么想都觉得还是一件挺划算的事儿。
画一幅简图来区分下我们这次同城双活的方案和业界异地双活方案的差异。
主要特点:
特点:
不管是同城还是异地、双活还是多活(双活只是多活里最简单的场景,双活到三活难度飙升范围应该不亚于<羊了个羊>里第一关和第二关的难度),都是为了以下目标:
一句话描述:在云机房的多个可用区(即多个物理机房)中构造应用层面的双集群部署,配合目前已经在交易链路大规模上线的蓝绿发布,完成流量的动态切换(含HTTP、RPC、DMQ[rocketmq/kafka])。而存储(redis/db)还是在单机房(但是可以跨机房部署),降低方案及实现的复杂度。
可以看到,整体在架构层面分为四层:
本次双活涉及到三个主要部分,分别是:交易应用侧双活改造、交易依赖方应用双活改造、中间件&基础组件改造。下面分别介绍:
A、C服务参与双活,需要跨可用区部署。B服务不参与双活,不需要跨可用区部署。 A、B、C服务都需要识别流量染色、服从流量调度。
完成上述改造后,双活链路上的流量呈现就近调用、可用区封闭的特点,即:流量染色后,后续链路上的每一跳调用都会优先向下游服务集群中与流量同色(同可用区)的实例发起调用。
仅仅依靠交易侧应用,无法完成所有的P0链路,如下单时依赖供应链侧时效。强依赖的外域服务同样纳入了同城双活改造范围。其改造点基本一致,不再赘述。
项目初期。我们发现容器POD和ECS缺少可用区标识,导致无法区分对应的资源归属。于是我们配合运维组和监控组的同事制定了一份规范。在环境变量里给机器都打上对应的标记,同时这也是监控和日志能透出机房标记的基石。
同城双活要求中间件在单个可用区出问题的时候,仍能对外提供服务。其设计目标的RTO为以下:
DLB是无状态组件,在两个可用区对等部署。
当其中一个可用区故障时,在SLB的endpoints上故障节点会被剔除,流量会打到正常的节点,实现故障快速恢复的目标。预计秒级完成。
彩虹桥目前不具备自动流量切换能力,一方面自动切换过于复杂,另一方面也容易带来更多的风险,以及也依赖DB层面的主备切换,所以走手动切换,预计分钟级完成。
目前流量99%走A区集群、1%的流量走B区集群,当A区发生可用区故障时,可手动把流量全部调度至B区集群,同时需要DB层完成主备切换(a->b)。
通过Broker分片级别打散到不同的可用区形成一套完整的集群。
当可用区故障时,集群可用分片会减少一半,集群整体可用。
DMQ的改造经过了多次试错,最开始通过在消费端创建多个consumer group的方式实现,但需要业务侧配合多次升级处理,且会导致消费端存在双倍的consumer group,后面才决定将主要改造工作放在rocketmq broker内部。简要介绍如下:
蓝绿属性
BROKER中的队列设定成偶数,并且>=2. 我们把前一半队列视为逻辑上的蓝色队列,后一半队列视为绿色队列(这里也可以看到,双活里的很多处理逻辑都是非此即彼,但是如果到多活,复杂度就会更高)。
生产者
在进行队列选择时,根据集群环境蓝绿颜色进行分组选择
a) 蓝集群的消息会被投递的broker的前一半队列中
b) 绿集群的消息会被投递到broker的后一半队列中
在每种选择逻辑内部是按照轮循的方式进行选择,不破坏生产者本身支持的容错逻辑。
消费者
消费者也是类似。蓝色消费者消费蓝色队列的消息。绿色消费者消费绿色队列的消息。
由于ZK的ZAB协议要求保证 Math.floor(n/2)+1 奇数个节点存活才能选出主节点,所以 ZK 需要进行3个可用区部署,上面的nameserver类似。分散在3个可用区中,A:B:C 节点数 = 2N:2N:1,确保始终是奇数个集群节点。
Broker 在两个可用区对等部署,分区的主从跨区部署。当单个可用区故障时,分区leader切换。
ES多可用区部署,需要区分数据节点和master节点。
注册中心
自研分布式注册中心,基于raft协议实现系统可用性、数据一致性。承担得物全站RPC服务发布/订阅职责。
双活的RPC的入口流量在DAG上进行调整,DAG会尽量根据用户ID进行流量分配。
因为蓝绿集群的生产者和消费者对队列进行了绑定。所以只要调整蓝绿生产者的消息比例就可以调整整个MQ的消费流量比例。而蓝绿生产者的消息比例一般由RPC流量决定。所以调整RPC的流量比例,MQ的流量比例也会得到相应的调整。不过会有一定的滞后(5-10s)。
2023年12月14日,筹备近100天的交易链路同城双活完成上线,经过5天(12.14-12.18)的观察及圣诞前高流量(DLB流量达到双十一的77.8%)的验证,确认无明显异常,之后线上集群完成缩容。部分场景的RT有一定比例的上涨(数据层面只做了跨可用区容灾,但是并没有实现就近访问,所以蓝集群的所有数据层面调用都需要跨可用区),已启动技术小项目推动优化中。
从实际效果上看,经过12.22的大版本发布过程中的跨机房切流,交易链路已经具备跨机房流量调度的能力,如下:
(A区 - 绿集群,B区 - 蓝集群)
因A区原有云资源均为包年包月模式,停止使用依然会有费用产生;同时在B区部署服务稳定性支撑50%流量之前,存在5天的并行期(A区100%资源、B区50%资源,共150%),期间共产少量成本。
灰度并行期结束后,A区资源释放掉50%,整体成本回归原有平均线,无额外成本产生。
6. 如何在线上无损情况下进行一次贴近实际的演练。
以上问题都是在双活之后带来的新挑战,也都在不断的思考及投入解决。
不管做什么,不管怎么做,人生总会有新的问题出现,不是么?Keep a long-term view lol...
*文/Alan 英杰 Matt 羊羽
本文属得物技术原创,更多精彩文章请看:得物技术官网
未经得物技术许可严禁转载,否则依法追究法律责任!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。