2014年,我在艺龙旅行网促销团队负责红包系统。

2014年,我在艺龙旅行网促销团队负责红包系统。
促销大战如火如荼优惠券计算服务也成为艺龙促销业务中最重要的服务之一。而优惠券计算服务正是采用当时大名鼎鼎的流式计算框架Storm。流式计算是利用分布式的思想和方法对海量“流”式数据进行实时处理的系统它源自对海量数据“时效”价值上的挖掘诉求。优惠券计算服务的逻辑是每个城市每个酒店的使用优惠券的规则并不相同当运营人员修改规则之后触发优惠券计算服务计算完成之后用户下单时在使用优惠券时会呈现最新的规则。优惠券计算服务是我们团队的明星项目很多研发的同学都对 Storm 特别感兴趣 , 因为 Storm 的核心开发语言是clojure 比较小众。于是在团队内部发现一个很有趣的现象很多同学的办公桌上放着《clojure in Action 》这本书。彼时艺龙开始发力移动互联网业务量的激增优惠券计算服务开始遇到了瓶颈。比如运营人员修改全量规则时整个计算流程要耗时一上午也就谈不上实时计算了。CTO 几次找团队负责人并严厉批责成他尽快优化。经过一个半月几次优化系统的瓶颈依然明显时不时运营同事会走到我们的工位附近催促我们“系统生效了么 ”我并不负责计算服务每当同事被质疑时我都感到很疑惑“优惠券计算服务真的那么复杂吗 ” 同时也跃跃欲试“Storm 真有那么难搞吗”我心中暗暗下定了决心一定要弄清楚优惠券计算服务的逻辑 。2 国图学习北京有很多景点都让我流连忘返比如史铁生小说里的地坛满山枫叶的香山如诗如画的颐和园美仑美奂的天坛 。在我心里有一处很神圣的地方它是知识和希望的象征那就是国家图书馆。中国国家图书馆位于北京市中关村南大街33号与海淀区白石桥高粱河、紫竹院公园相邻。它是国家总书库国家书目中心国家古籍保护中心同时也是世界最大、最先进的国家图书馆之一。每到周末当我想安静下来专注思考时我就会背着笔记本电脑来到国家图书馆。选择自己喜欢的书然后将笔记本电脑打开一边看书一边在电脑上写点笔记。偶尔抬起头望着那些正在阅读的读者心里面感觉很阳关觉得生命充满了希望。我并不负责流式计算服务但想要揭开 Storm 神秘面纱的探索欲同时探寻优惠券计算服务为什么会这么慢的渴望让我好几天晚上没睡好。于是周六上午9点半 我来到国家图书馆 想让自己安静下来思考如何解决这个问题。解决问题的快感是我一直追求的。当我把笔记本电脑放平在桌上我很兴奋同时灵台一片澄清优惠券计算服务的核心是 Storm 那么我需要先了解 Storm 的整体概念。打开官网浏览官网的文档第一次看到 Storm 的逻辑流程图时做为程序员我第一次竟然感受到抽象之美从源头流下来的水通过水龙头Spout再经过层层转接头(Bolt)过滤不就是我们想要的纯净水吗其实我们原来都是CRUD boy机械的使用那些框架只会做增删改查并不会思考框架背后的设计思路。 但框架到底是什么从来没有思考过。 我一直觉得我很笨拙学什么都很慢但那一刻我突然恍然大悟框架本身是将解决问题的思路抽象化从而便于研发人员使用把复杂的问题抽象成有美感是需要功底的。了解完 Storm 整体概念 下一步也就是大家熟知的写 Hello World阶段了 。我参考教程写了一个简单的 Storm 应用简称拓扑在部署后程序正常跑了起来。我脑海里一直有一个疑问“是不是优惠券计算服务的 storm 集群的配置没有调优才导致计算的性能太差 ? ” 所以我必须去理解 storm 的并发度是如何计算的。整个下午我一直在查阅相关的资料并结合下图思考Nimbus Supervisor Worker Task 这些名词到底是什么概念以及他们之间是如何交互的。进而思考拓扑到底会启动几个进程每个进程内部线程模型是怎样的颇有些庖丁解牛的味道。这个习惯一直保持到现在当我看到一个系统我会下意思的去思考“这个系统的线程模型如何每次操作有哪些线程参于他们之间如何交互”。我知道有更厉害的大牛运行一行代码就知道 CPU 会运行的哪些指令我做不到但我觉得那就更加深刻了。不管怎样这一天我的思绪经过多次的变化兴奋犹疑放弃阳光激动畏难心理一直存在很多次想放弃但好奇心一直鼓励着我。等天色已黑我走出国图的大门脑子里全部都是 Storm 进程线程模型内心里面有了莫名的自信。感觉自己就像仙剑奇侠传里的酒剑仙伴随着激昂的 BGM 拔剑四顾斩妖除魔。御剑乘风来除魔天地间有酒乐逍遥无酒我亦癫。一饮尽江河再饮吞日月千杯醉不倒唯我酒剑仙。3 找到瓶颈当我理解了 Storm 的整体概念接下来我需要去找到优惠券计算服务的性能瓶颈。这个时候梳理计算服务整体流程非常关键。计算服务整体流程分为三个步骤 抽取数据酒店信息拉取服务拉取酒店信息并存储到水源头( Redis A/B 集群 ) ;计算过程Storm 拓扑从水源头获取酒店数据通过运营配置的规则对数据进行清洗 将计算好的数据存储到水存放池( Redis C 集群) 入库阶段入库服务从水存放池获取数据将计算结果存储到数据库 。当我们把整个计算的过程拆分成抽取--计算 -- 存储三个阶段的时候计算服务的架构就变得异常清晰那到底在哪个阶段最耗时 也成为我追查的目标。优惠券计算服务当时没有详细的性能监控体系所以我只能先从日志着手。 在运营同事触发全量计算后分别观察三个阶段对应服务的日志抽取数据酒店信息拉取服务计算过程:Storm 拓扑入库阶段:入库服务令人惊讶的现象一次全量计算需要耗时4个多小时但抽取数据的任务竟然跑了2个多小时和我预期完全不一样。假如我把酒店信息拉取服务比作抽水泵那么整个系统最大的问题竟然是抽水泵抽水马力不足。4 推进重构为什么抽水泵抽水马力不足 通过阅读源码我发现因为线程模型不够好应用在部署多个节点后每个节点只能有两个线程执行拉取酒店信息。怎么处理呢 在原有代码上优化可行吗 好像也不太容易因为老代码最初是一个 C# 研发同事写的他当时也不熟悉 JAVA 从设计层面来讲有很多冗余且不合理的代码而且经过3年左右的维护代码老化严重于是我只能想到重构。当我把想法和团队负责人沟通后他有点半信半疑他认为我的判断没有问题但不确定我是否可以将系统重构好。 我那时候信心爆棚主动请缨打包票不会出问题的。可能是由于 CTO 逼的太紧了他同意了。在重构之间梳理好系统的整体逻辑。重构的重点原则有两条拉取服务可水平扩展若性能不足时增加服务节点即可提升性能配置文件可配置 worker 线程数量。那思想层面我已经做好准备了那硬实力层面我有没有做好准备吗 非常自信的讲准备好了因为我遇到了 RocketMQ。我在《我与消息队列的8年情缘》这篇文章写到2014年我搜罗了很多的淘宝的消息队列的资料我知道MetaQ的版本已经升级MetaQ 3.0只是开源版本还没有放出来。大约秋天的样子我加入了RocketMQ技术群。誓嘉(RocketMQ创始人)在群里说“最近要开源了放出来后大家赶紧fork呀”。他的这句话发在群里之后群里都炸开了锅。我更是欢喜雀跃期待着能早日见到阿里自己内部的消息中间件。终于RocketMQ终于开源了。我迫不及待想一窥他的风采。因为我想学网络编程而RocketMQ的通讯模块remoting底层也是Netty写的。所以RocketMQ的通讯层是我学习切入的点。我模仿RocketMQ的remoting写了一个玩具的rpc这更大大提高我的自信心。正好艺龙举办技术创新活动。我想想要不尝试一下用Netty改写下Cobar的通讯模块。于是参考Cobar的源码花了两周写了个netty版的proxy其实非常粗糙很多功能不完善。后来这次活动颁给我一个鼓励奖现在想想都很好玩。在重构酒店信息拉取服务时我将 RocketMQ 如何创建线程的知识点正好也用了上去并学习如何将模块拆分得更加合理。同时在重构过程中不断 Review 新老代码的差别确保核心逻辑正确。非常幸运大概一周时间我就重构完了。重构完成并不意味着结束怎么验证呢 我当时采取了两种方式代码评审我拉着优惠券计算服务的同事一起 review 代码 。整个过程大家也并没有提出异议并对我创建线程的技巧感到很好奇。我心中窃喜”那是学习 RocketMQ 的“。测试环境数据验证我们将新旧两版服务同时触发比对两个版本的数据的异同将比对结果输出到日志文件然后从中找到差异的地方修复重构版的 BUG 。 然后在测试环境部署重构版观察一段时间确保无异常。从编写第一行代码三周时间重构版终于上线了。 我将原来的老服务替换后部署了3个节点 每个节点8个 worker 并行拉取酒店信息 。令人开心和激动的是重构是非常成功的。 因为业务给我们的时间需求也是1个小时左右。一次全量计算从原来4个小时急速缩减到1小时15分钟整个酒店拉取服务耗时40分钟左右。我心里长舒一口气内心吟诵李白的诗十步杀一人千里不留行。事了拂衣去深藏身与名。5 向前一步前 Facebook COO 谢丽尔·桑德伯格 写了一本书《向前一步》我特别喜欢这本书的书名 。在优化优惠券计算服务的前期团队经过一个多月的时间也没有什么成效。 我自己也犹豫”我能不能解决这个问题“ 但最终我还是向前一步并帮助团队大大提升了服务的性能负责人也有了信心他也敢投入资源优化Storm 拓扑和入库流程。在阅读优惠券计算服务的代码中我发现两个问题