本文主要介绍知乎订单系统后端语言栈的转型升级过程,包括其间踩过的一些坑和遇到的一些问题。一来是想通过本篇文章为其它应用服务转型提供借鉴经验,二来是总结对于订单系统的理解。鉴于文字功底不足,对于业务理解不充分的地方,欢迎留言交流。
迁移背景
随着知乎整体技术栈的变化,原有的 Python 技术栈逐渐被抛弃,新的 Go 和 Java 技术栈逐渐兴起。知乎交易系统的稳定性相比其它业务系统的稳定性重要很多,因为交易系统核心链路发生故障不仅会造成数据问题,还会造成严重的资损问题。
随着公司业务的不断壮大发展,交易场景变得复杂,重构和优化难以避免,因为语言特性,Python 虽然开始撸代码很爽,但是后期的维护成本慢慢变高,不过 Python 在数据分析和人工智能方向上还是有很大优势的,只是在交易领域目前看起来不太合适。从技术生态上来说,用 Java 做交易系统会更有优势,所以接下来要说的知乎订单系统语言栈转型。
另外一个因素是 Python 的 GIL 锁导致它无法发挥多核的优势,性能上受到很大限制,在实际情况中遇到过多次主线程被 hang 住导致的可用性故障,所以坚定决心来迁移掉旧系统。
前期准备
工欲善其事,必先利其器。
语言栈转型首先要明确转型的三个开发流程,即 MRO (Migration, Reconstruction, Optimization)
迁移 就是把原语言代码照着抄一遍到新语言项目上,按照新语言的工程实现风格来做就可以。其间最忌掺杂代码优化和 bug 修复,会容易引起新的问题,增加验证代码的难度。
重构 目的是提高项目代码的可维护性和可迭代性,让代码更优雅和易读懂,可以放到迁移完成来做。
优化 通过在模块依赖、调用关系、接口字段等方面的调整来降低项目的复杂性,提高合理性。
对于语言栈转型来说,迁移流程是肯定要做的,重构和优化如何选择,可以按模块划分功能拆成子任务来分别评估方案,参考依据为现有模块如果同时优化或重构带来的直接收益和间接收益有多少。
收益:完成新旧语言栈的转换,系统维护性更好,模块边界更清晰。
成本:需要投入的人力成本,迁移过程中的并行开发成本,使有更高价值的工作被阻塞的损失。
风险:引入新的 bug,增加测试的复杂性。
在风险可控的前提下,成本与收益要互相权衡,一般会有两种方案可供参考:第一种是锁定需求,堆人力开发上线,一步到位;第二种则是小步快走,迭代上线,分批交付。
基于以上分析,在本次转型过程中,人力成本是一个更重要的因素,所以采用只迁移的方案,来压缩人力成本,降低 bug 引入风险的同时也具有很好的可测试性。并且为了不阻塞业务需求,采用小步快走的方式分批交付,以最长两周作为一个迭代周期进行交付。
迁移方案
确定了交付方式,下面我们需要梳理当前系统中的功能模块,做好任务拆分和排期计划。知乎交易系统在迁移前的业务是针对虚拟商品的交易场景,交易路径比较短,用户从购买到消费内容的流程如下:
在商品详情页浏览
生成订单进入收银台和用户支付
确认支付后订单交付
用户回到详情页消费内容
特定商品的七天无理由退款
当时订单系统支持的功能还不多,业务模型和订单模型没有足够地抽象,梳理订单系统业务如下:
完成了订单模块的拆分后,新老系统如何无缝切换?如何做到业务无感?如何保障交易系统稳定性?出现故障如何及时止损?基于上面讲述的原则,将整个系统的迁移划分成两个阶段,迁移前后的数据存储和模型都不变。
接口验证
不论是在迁移的哪个阶段,总需要调整订单接口,可以从订单操作角度分为读操作和写操作,需要针对读接口和写接口做不同的验证方案。
写操作可以通过白名单测试以及灰度放量的方式进行验证上线,将接口未预期异常输出到 IM 工具以得到及时响应。主要的写操作相关接口有:
订单的创建接口。
订单绑定支付单的提交接口。
用户支付后回调确认接口。
用户发起退款接口。
下图展示的是 AB 平台的流量配置界面:
下图展示了部分交易预警通知消息: