聊聊流式核算吧 , 那一段阅历于我而言很精彩,很风趣,想把这段阅历共享给咱们。
1 背景介绍
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 拓扑和入库流程。
在阅览优惠券核算服务的代码中,我发现两个问题:
- 流式核算逻辑中有很多网络 IO 恳求,主要是查询特定的酒店数据,用于后续核算;
- 每次核算时需要查询根底装备数据,它们都是从数据库中获取。
关于Storm 拓扑优化,我提了两点建议:
- 流式核算拓扑和酒店拉取服务各司其职,将流式核算中的网络 IO 恳求挪到酒店拉取服务,将数据前置预备好;
- 根底装备缓存化,引进读写锁(也是 RocketMQ 姓名服务的技巧)。
关于入库流程,一位研制同学将本来的单条数据入库修正成批量入库。
通过咱们一起努力 ,优惠券核算服务的全体功能大大提高了,全量核算耗时现已变成40分钟了,再也不会有运营搭档在咱们的工位邻近吐槽体系慢了。
6 写到最终
2014年,我向前一步推动了公司流式核算服务的优化,并取得了一点点进步。
时光荏苒,我已中年,生射中遇到越来越多的挫折,有的时分也会让人失落,但每当想起这个故事,我会深深感动于其时的一往无前。
当再次面对挑选时,我希望自己也能够向前一步,想着怎样协助读者生长,或是完成一个产品协助更多的人。
如果我的文章对你有所协助,还请帮助点赞、在看、转发一下,你的支持会激励我输出更高质量的文章,十分感谢!