饿了么交易系统 5 年演化史

Photo @ Bluehouse Skis 

文  |  挽晴

个人简介:

2014年12月加入饿了么,当时参与后台系统的研发(Walis+Javis=>Walle),主要面向客服和BD。

2015年5月开始接触订单系统的研发,7月负责订单研发组;度过单体应用到服务化这个阶段。

2016年初搭建订单的测试团队,订单拆分为正逆向后,主要负责正向和交付部分。

2017年做了一些平台搭建的探索。

2018年初负责整个订单正逆向和交付,年中将下单、购物车部分一起归并,年底和商户订单部分整合,形成交易中台。

2019年10月从交易中台转出,近期做了一小段时间的组织效能和架构。

我为什么会写这篇文章,究其缘由:

 

一是自己在交易域做了 4 年,有很多只有我才知道,才能串起来的故事,想把这些记录并保留下来。

二是发现后边的很多同学看交易体系时,一接触就是分布式、SOA、每日百万、千万数据量,只知道它是这个样子,很难理解背后的思考和缘由。伴随自己这几年的经验,想让大家能够更容易的理解这个演化过程的原因和历程,有甘有苦。

三是很多总结也好,方法论也好,更多是去除了“糟粕”呈现在大家面前,这里可能会稍微加一点“毒鸡汤”,现实不一定那么美好,我们有很多抉择,现在回过头来看,也许是庆幸,也许是错误。

 

这篇文章希望通过一些发展的故事和思考来给读者呈现整个历程,大家可以看到非常多野蛮生长的痕迹,并会附带一些思考和总结,但不会像快餐式的总结很多大道理。

那我们就从2012年的太古时期讲起。

 

太古


在谈订单之前,我们往前再考古考古,在太古时代,有一套使用 Python 写的系统,叫做 Zeus 的系统,这个 Zeus 包含了当时饿了么最核心的几大模块,比如订单、用户、餐厅,这些统统在一个代码库中,并且部署在同一台机器, Zeus 之外还有两大核心,即饿了么 PC ,也就是很多老人常提的「主站」,以及面向商户的 NaposPC 。这些系统通过 Thrif 协议通信。除开这条链路之外,所有杂乱的内部功能,全在一个叫 walle 的系统中,这个 Walle 系统是采用 PHP 写的。

 

那么当时的 Zeus ,大概长这个样子:

                                

 据不严格考究,从 Git 的提交历史看,订单部分的第一个 commit 是余立鑫同学于 2012 年 9 月 1 日提交的,内容是" add eos service for zeus. currently only defind a simple get api. ",这个 EOS 指的就是订单系统,即 ElemeOrderService 的简称,这个名词沿用到了今天,成为交易正向的订单部分,甚至一段时间是订单组的代名词。

 

 Zeus 在后来其实经过了一定的重构,叫做 Zeus2 ,但具体时间已不可考。

 

萌芽


2014 年 10 月我到饿了么来面试,面试官是商户端负责人磊哥。 12 月 1 日,我入职饿了么, HR 领着带着一脸萌新的我,到磊哥面前时,磊哥把我带到 JN 面前说,“这就是那个实习生”,然后扭头就跑了。后来得知,当时面试结束后,磊哥和 JN 同学说,刚刚面了一个实习生,凑合能用,正巧商户组有计划转型 Java ,而佳宁还很缺 python 的人,然后就骗了 JN 一顿饭把我卖了。

 

回到正题,在 2014 年 12 月~ 2014 年 4 月这几个月的时间里,我配合完成了一个更老的 BD 系统后端迁移到 Walis ,并且在我的导师转岗到 CI 团队后,自己完成了 Walis 从单应用迁移到分布式应用。

 

订单组的成立

 

对我来说,完全是运气和缘分...

  

接近 2015 年 5 月的时候,我的主管,JN同学,有一天突然找到我,看起来很兴奋,告诉我,公司打算成立一个订单组,这个订单组由他来负责,除了他之外,他唯独选中了我(大概是因为上段我提到的一些经历,在可选的人里,还凑合~),说是我怎么怎么让他相中,这个男人忽悠起人来,一套一套的。

 

作为一个技术人员,内心非常沸腾。一是高并发、高流量、分布式这些耳熟能详的高大上名词之前只是听说过,不曾想这么快就能够接触到这样的系统;二是我们此前做的系统很“边缘”,有多边缘呢,白天几乎没什么请求, BD 走访商户回来,恰巧晚上才是高峰期,即使是晚上,关键的单接口也就偶尔几个、十几个请求,是当时那种挂 2 个小时才可能有人发现,挂半天不一定有人叫的系统,那时候我们幸福的晚上 7 点前就下班了,第一次发布的时候非常郑重的和我说,可能要加班到晚上 8 点半。

 

之所以选择 JN 做订单组负责人,因为他虽然是个前端工程师起家,做的是“边缘”后台系统,但却是对整个公司所有系统和业务都比较熟悉的人,很适合发展订单系统。

 

嗯,没错,这个组在成立前一天,一直只有我们两个人。当时的我还没毕业,除了兴奋,更多的是忐忑。

 

2015 年 5 月 12 日,订单组正式成立,成立当天,拉来了隔壁组的 ZH (是个PHPer,招进来的时候是计划去接Walle),然后聊到一半的时候,当时的部门总监跑过来,说正巧有个小哥哥当天入职,还不错,正好给订单组吧,是个 Java 工程师。于是乎,成立当天,我们人数翻了一倍,变成了 4 个人。

 

我们给自己的第一个任务: 读代码,理业务,画图。和 CTO 申请到了 1 个月的时间来缓冲,这段时间不接任何业务需求!

分别请来了订单的前主程、Python 框架负责人、Zeus 系应用运维负责人给我们讲解。实际上,每个人的分享也就 1 个多小时。那一个月真是从几万行 Python 代码,没有任何产品文档,极其稀少的注释,一行行的啃,每个人解读一部分。我最后汇总把整个订单的生命周期、关键操作、关键业务逻辑,画在了一张大图里,这张图,我们后来用了一年多。

 

其实,当时年中旬的饿了么,产研规模已经达到几百人左右,新 CTO ,雪峰老师是年初加入饿了么,整个基础设施的起步是 2015 年下半年,整个体系的飞速搭建是在 2016 年。

       

可以说是正处于相当混乱,又高速发展的时期。我们称那个时间是一边开着跑车一边换轮胎。

 

Zeus 解耦

 

和订单真正密切相关的第一个 Super 任务,大概是从 6 月左右开始 --- Zeus 解耦,HC老师是 Python 框架的负责人,也是个人最佩服和敬仰的技术专家之一,在美国举行 Qcon 上,作为首席架构师介绍过当时饿了么整体技术架构。刚才在太古时期已经说到, Zeus 是一个巨型单体应用,为了今后各个部分能够快速发展,降低耦合和牵连影响等,公司启动了 zeus 解耦项目,总之就两个字,拆分

      

经过 1 个多月的密集会议,完成了拆分的方案。说的似乎没那么难,但是这场口水战当时打的不可开交,拆分后不同的服务归属于谁?模块和模块之间并没有切分的那么干净,A和B服务中的边界怎么定等等一系列问题。当时的我还不够格参与讨论。

 

结论是, Zeus 将要拆分成下边的几个主服务:

 

  • zeus.eos => 订单服务

  • zeus.eus => 用户服务

  • zeus.ers => 商家服务

  • zeus.eps => 营销服务(新产物)

  • zeus.sms => 短信服务

  • ...

 

第一阶段

每个被拆分后的服务,随之进行的是新的一波重构和拆分。例如从 zeus.eos 分离出来 biz.booking ,拿走了下单和购物车部分能力;分离出来 biz.ugc 拿走了订单评价相关能力。

       

拆分主要经历的几个阶段:

1、(7月份)共享代码仓库,按模块独立运行。即,把 Zeus 所有代码都打包到服务器后,按照划分,在特定机器上只将特定模块单独启动,开放特定端口。

2、(8月份) Proxy 阶段。即在原服务中,要迁出去的接口上增加一个代理,可以代理到新服务的接口,由服务注册中心开关能力来控制切换流量大小。

3、(8月份至9月初)脚本、模块的完全切分改造。

4、(9月份)代码仓库独立。使用了 Git 的核弹武器 filter-branch ,将模块中的代码和变更历史,完全完整的从原代码库中分离。而此时部署却仍然为混布,在发布工具中,某个独立应用发布后实际是替换了 Zeus 这个大项目下的某个目录。

5、(9月份)配置独立。原来的配置由 saltstack 刷到服务器上,被服务器上多个应用所共用,我们将其直接改成使用服务注册中心的配置下发能力获取单个应用配置。在这个阶段也基本上过渡到了软负载。

6、(次年3月份)物理部署独立。当然这是解耦二期的内容了。

 

当然,这次拆分,还带来了另外一个产物, Python 的 SOA 框架 zeus_core,zeus_core 要大概在 4 月份左右先于业务服务被拆分出来。

 

整个解耦一期,持续了大概半年时间。在期间,没有发生因为拆分导致的事故,也几乎没有什么冒烟。想想当时没有用什么高深的东西,工具落后,没有专职测试,完全靠着一帮早期工程师和运维同学的技术素养。

 

分库分表

 

仍然是在 2015 年,大概是 9、10 月左右确定分库分表要开始实施,而分库分表的方案,在我介入时已经几乎敲定,并由 CI 部门的 DAL 团队主导。

 

为什么要做分库分表?

 

一是扛不住并发。当时我们的订单库的 MySQL 是采取 1 主 5 从的架构,还有 1 台做 MHA 。DB 不太能承受住当时的并发压力,并且,对风险的抵抗能力非常的弱。业务如果做一些活动没提前告知,我们的从库一旦挂了一个,就只能来回切,严重的时候只能大量限流。而且,那段时间,作为技术,我们也在祈祷美团外卖别在高峰期挂,美团外卖一旦挂了,流量就会有一部分流到饿了么,我们就开始也紧张起来了。同样的,那段时间,我们整站挂了,美团外卖也不太能扛得住,大家都在经历相似的发展阶段。

 

二是 DDL 成本太高,业务又处于战斗高峰。当时饿了么的单量在日均百万出头。有一些业务需求,希望在订单上新增字段,然而,我们找到 DBA 评估的时候,给的答案是,乐观估计需要停服 3 小时,悲观估计要 5 小时,并且需要 CEO 审批。显然,这个风险,技术团队难以接受,而业务团队也无法接受。那么投机取巧的方案,就是在预留的 Json 扩展字段中不断的塞,这种方式一定程度上缓解了很长一段时间的压力,然而,也埋下了非常多的隐患。

 

当然,还有一些特殊的业务场景以及一些开放出去颗粒度很大的接口,会产生一些性能极差的 SQL ,都会引爆全站。

 

Shardin 后物理结构如下:

 

一次更新操作逻辑如下:

                       

   

我们其实是做了两维 Sharding ,两个维度都是 120 个分片,但是可以通过三种方式路由(用户 ID、商户ID、订单ID),写入优先保证用户维度成功。由于资源的原因,用户和商户分片是交错混合部署的。

 (加粗部分其实是有一些坑的,这个特殊定制也是饿了么唯一,如果有兴趣以后可以展开)

 

更具体分库分表的技术细节不在这里展开,大致经历了几个阶段:

 

1、制定新的订单号生成规则,并完成改造接入。

2、数据双写,读旧,对比数据。

3、对不兼容的 SQL 进行改造,比如跨分片的排序、统计,不带shardingkey的SQL等等。

4、数据双写,读新。(与3有部分同步进行)

5、完成数据库切换,数据写新读新。

 

这段日子,作为业务团队

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值