50天10万行代码,一号专车系统重构细节回顾

转载 2016年08月31日 13:39:26

2013年底,我关闭当时的创业项目,无所事事之时,打电话向快的CEO Dexter请教,当时快的和大黄蜂刚刚合并,他建议我可以先和大黄蜂CEO李祖闽(Joe)聊聊。

和Joe第一次见面是在虹桥火车站的一家肯德基里碰头,当时我看不太懂打车这个项目。那次碰面,我们聊的却不是出租车,聊的全是专车。两年多前的那时候,做这一块的不多,真正意识到这是个大机遇的且投入资源的创业公司比较少。Joe跟我讲了许多他对未来专车的构想,一下子就挑起我非常大的兴趣,简单说就是High了:)。

Joe也跟我讲了现在公司在技术上碰到的一些瓶颈,限制了业务的开展,比较痛苦,我们原定半个小时的碰面,结果一聊聊了4个多小时后,两人意犹未尽,又一起打车回市区聊了一路,下车的时候我和他约好第二天就去大黄蜂报到。

故事就这样展开了。。。

定时炸弹

2013年12月26日,刚入职的第一天,我看到了打车大战满地销烟的惨状,快的、滴滴、大黄蜂等多家打车公司刚刚在上海打过几波大战,当时大黄蜂打车在上海市场出租车市场的占有率可以排进前三。快的大黄蜂合并后,虽然明确大黄蜂未来的战略方向转做专车业务,同时也上线了第一版本的专车系统,并将部分出租车请求转发给快的打车。但出于种种因素考虑,还是把一大部分的工作重心依然留在出租车上面,并没有完全放弃出租车业务。

我初进大黄蜂,又恰逢临近春节,专车系统刚刚上线不久,我们想要在上海做一次“春节50/100元专车接送机场(虹桥、浦东两个机场)”的活动,但原来的产品及部分开发人员出现了一些波动,正在陆续办理离职手续,留下的开发人员状态也不太稳定,好在专车系统的即将离职的同学还比较靠谱,被我拉着讲了一个周末的数据库表结构及代码,于是我就是在边看代码边熟悉系统的基础上,进行业务开发,技术总监写代码不算什么,其实后面重构上线前期我还做过一整周的测试

2014年春节很快过去,快的和滴滴在全国掀起一波补贴大战,大黄蜂的出租车业务在上海市场协同快的也侧面参与了一些,但大部分精力已经开始转向专车业务,我对原来的系统也已经比较了解,当时的系统情况大致是这样:

当时出租车主要开了两个城市,上海和广州,各部署一套系统,数据库、缓存、Web服务、API、定时任务都在两地部署,由中心库负责定时同步主要的数据,比如用户信息等,但因为两地市场开拓的时候,运营策略不同,比如各项优惠、加急令、司机补贴等政策在两地各有不同,于是就维护了两套PHP代码,大致逻辑相似,细节又各有不同。

而专车系统是后来开发,以Java为主要开发语言,基础数据(用户、优惠券、订单等)依然依赖于出租车数据库,但主要数据库和代码部署在上海,因为专车只开了上海市场,所以暂时没有什么影响。这样的系统架构限制了当时的业务发展,也给未来留下了许多定时炸弹,下面我们会展开讨论。

但在这里需要申明一点:看到这样的系统结构,确实存在许多不合理性,单纯一张九十几个字段订单表就能干掉一大票观众,但我要为其辩护,在当时打车大战初期,业务的发展是野蛮式的,每天各种补贴政策、活动的调整,开发人手严重不足,技术完全是被业务拖着跑,相信经历过创业这个阶段的人都会有深刻感触。

很显然,这样的系统架构已经不能满足当前的业务需求,越往后拖,隐患越大,定时炸弹越多,我评估下来,主要存在这么几类大的问题:

1、多地保存数据,多地多份代码不可维护。

  • 多地逻辑代码冗余:代码无法维护和管理,添加一个业务逻辑要同时改两份码,或者只改一份,但下一次增加新业务逻辑时,因为两个地区的代码逻辑差异越来越大,需要非常小心,坑越挖越深;

  • 数据同步问题:上海的乘客到广州不能下单、优惠券不见了,反之亦然,上海的乘客出差去外地后,也不能下上海的机场、车站的预约订单,甚至不能登陆;

  • 并发冲突:两套数据的维护,一不小心就出现数据不一致的问题,无法合并,也留下被攻击的隐患;

2、无法快速开城,每开一个城市都要部署一套存储和代码,准备硬件、进机房、部署、调试,效率太低,还要做好数据的同步,及当地的新政策的业务开发,开一个城市需要一个月左右的开发周期。

3、数据库表结构和代码许多地方都是堆出来的,不可维护,比如一张订单表有九十几个字段;PS: 你没有看错,就是九十几个字段。

4、手机端通过轮询接口调用API,为了快速获取成单、通知、补贴政策调整等信息。但毫无疑问,这个对于手机的资源消耗(流量、电量)很大,也增加服务器的开销。

5、公共服务和业务代码耦合太紧,比如优惠券、支付等模块;

6、专车司机公里数计算不准确,出租车司机带有打表器,专车司机主要靠手机GPS,手机会有信号不准,接受不到GPS(跳点、断点、隧道、高架)等情况;

7、系统压力大,活动一上来,慢查层出不穷,各个状况出现;

8、API接口没有加密机制,容易被刷,用户身份容易被窃取;

9、无法适应专车灵活多变的业务场景,每做一个活动,都需要2,3周的开发;

还有许多细节,比如轮询接口直接访问数据库、GPS坐标系混乱、司乘价格绑定、重复上报GPS、电子围栏不可维护、支付、定位、保险、第三方接口接入等,已不能满足业务发展,这里就不再一一列举。在这样的前提下,内部反复多轮沟通后,我们启动了重构计划。

和时间赛跑的重构马拉松

2014年4月9日,我们正式启动了重构,重构目标就是在解决这些问题的基础上,完成整个系统的第一阶段的服务化工作,大致目标包括有:

  • 设计一套灵活的专车系统,以应对专车复杂的业务场景;

  • 整个系统,包括存储及服务集中部署,便于管理维护;

  • 需要支持出租车、专车两项业务的迅速开城;

  • 手机端和服务器的实时消息(包括接单、抢单、成单、通知、计费等)推送采用长连接;

  • 接口定义一套的新的数据协议,请求加密,防token窃取,请求防篡改;

  • 采用多级缓存方案,数据库、Redis、MongoDB、分布式本地缓存;

  • 引入分布式日志系统(重构后期来不及加入,只做了一部分,但后期为此吃足苦头,快的的兄弟们给了我们很大的帮助);

  • 公共服务及业务服务的抽象及服务化;

  • 南、北向接口分离,南向接口主要对接外部供应商,北向接口仅指出租车部分,主要面向第三方渠道、比如携程、去哪儿等。

  • 数据库分库、分表、读写分离;

  • 优化寻找周边司机,道路距离计算等算法;

  • 上阿里云;

第一阶段的服务化设计后的架构图,大致如下:

上图中大部分的设计都实现了,并取得了不错的效果,但也埋了坑。其中订单服务在重构中期因为出租车业务的停止,为了贪图调用方便,又把它合到专车核心服务,后续在订单量爆发的时候,为此吃足苦头。出租车司机API也因为方向问题停止维护。

在当时的环境下,重构这样的一个系统主要有这么几大挑战:

1、 时间不够:项目计划梳理需求和旧有系统逻辑2周,开发&测试10周,上线2周,预计在7月底左右上线。

2、 开发资源严重不足:前线的还在继续作战,原来系统还需要有人留下做维护性开发,真正能抽出来做后端系统重构的开发人员只有5个(包括我),4个Java开发,1个PHP开发,其中有3个都是新招的开发人员。APP开发也只有4个人参加重构,同时负责iOS、Android。

3、 对公共模块业务不熟悉:因为专车系统是搭建在原出租车系统之上,所以大部分公共模块都依赖出租车业务,而原出租车业务的开发人员流动比较大,代码也过了好几手之后,留下来的人员对其业务细节,遗留代码并不了解,这将对后续的数据迁移留下很大隐患;

4、业务复杂:如何设计一个灵活多变的专车系统,以适应快节奏的运营策略。专车因为其特性决定了它的业务复杂多变,随便举几个例子:

  • 策略1:希望做接送机场订单一口价的活动,春节前,终点为虹桥/浦东机场,起点为全上海任意地区的,可享受50/100元一口价,春节后,起/终点倒置,为回程的乘客服务;

  • 策略2:当某种车型不够用时,只有下机场单或者特定的好用户时可以看到;

  • 策略3:高峰期时,高级车型可以向下接单,但司机收入不变,给抢单积极的司机更好的订单;

  • 希望在上海内环以内做一口价活动,20元一口价,随便打车,但司机价格会根据活动情况分等级调整;

  • 策略4:好的预约订单给抢单积极的司机,好的即时订单给近的司机;

  • 策略5:多加一个专车产品,带有婴儿椅的车子,女性乘客可以打到;

  • 策略6:打一个专车,如果时间超过2个小时,希望变成包车服务,但价格要有给乘客优惠;

  • 策略7:我想让某个起点的乘客享受费用折扣,甚至免单,但平台按正常价格给司机计费;

  • 策略8:每开一个城市,我想对机场、火车站的订单实行一口价策略;

  • 策略9:在某些城市,有一部分司机是合作司机,一部分加盟司机,两者计费规则完全不同,需要满足;

基本上,这样的策略每个月都有,全国几十个城市,每个城市可能还不同,如果需要通过开发来满足这样的需求,那么几百人的开发团队都不够用,运营时间上也等不了。所以,如何设计一个高可用,灵活多变的系统来适应业务的需求,这是一个非常大的挑战。

5、技术挑战:剩下还有就是一些技术上的挑战,比如快速搭建一套分布式服务框架,如何快速找到周边的司机、如何通过道路拟合比较准确的计算道路距离、长连接的QoS如何保证、司乘两端心跳上报异常如何处理、如何防止心跳风暴、规则引擎的性能优化、发单/抢单处理队列的守护机制等。

如何解决这几个难题呢?

先说开发资源吧,开发资源属于硬伤,创业公司前期招人,招到好的人比较难。业务等不了,有句话说的好“有条件上,没有条件创造条件也要上”,这就是当时的情况,人就这么多了,边做边招吧,后面两个多月我们陆陆续续招了六七个开发人员,但新招聘的大部分经验还比较浅,但也基本上顶过去了。

再说到公共模块,当我入职一周左右的时候我就知道系统必须大规模重构,但当时因为种种因素提重构必然会给业务、团队造成比较大的影响,也会影响刚刚合并的团队信心,所以前期更多是花精力在整合团队和拖着专车系统往前走。

公共模块的代码都是在出租车业务系统,向原来的开发人员了解细节时,被告知许多历史遗留问题已不可追遡,只知道不要轻易去动,一动就崩。于是,重构计划开始后,我挑了一个对原出租车业务比较了解的哥们,拉着他两个人慢慢翻PHP代码,翻数据库表,通读一遍,才开始抽象公共模块及进行数据库设计。

其次,说到专车系统灵活多变的设计,我首先把专车业务的预估、选择车型、发单、抢单、撮合、做单、计费、通知等流程环节做了抽象,并固化,把可变的部分剥离。

  • 比如预估算价、显示车型、计费时将司机和乘客完全分开,实际上在业务抽象的角度来看,专车的乘客和司机发生的交易对象都不是对方,而是平台,这样就平台在运营过程中就具备非常灵活主导权;

  • 比如消息通知模块做了抽象,把不同阶段消息体抽象成模板,模板中带有变量,举个最简单的例子:如参加某个活动下单的用户和普通下单用户,在下单或计费时收到的短信内容不同,但实际只是短信模板不同,中间的价格用变量替换,有的甚至不发短信;

  • 比如某些特定场景,第一轮发单不希望发给某类司机,要第二、三轮才发给他,撮合成单时也根据不同维度的指标进行排序定义;

  • 比如将优惠券可用在不同业务场景定义成模板,再模板上增加Scope,Scope又反作用到产品上面;

  • 比如,对专车运营中最经常发生变化的预估、可见车型、发单、抢单、撮合逻辑,通过将乘客和司机的特征指标抽象,这个乘客、司机、订单三个纬度的特征指标抽象很关键,在后期和大数据结合,也可以将乘客司机的画像指标引入,再利用规则引擎来配置每个环节的变化,见下图:

    在将以上所有这些灵活配置的内容之上,又定义了大小产品的概念,把这些灵活多变的配置参数抽象成一个个产品,每个产品维护这些配置的参数或规则,从而达到运营策略和运行系统的隔离,实现了一套灵活的专车业务系统。

这些产品对应给乘客看到的其实是一个个车型,但看起来同样是经济型的车,在后台根据不同的条件已经被抽象配置成不同的定义规则,如机场订单的经济型和非机场的经济型就完全是不同的业务规则,无论从预估的价格、发单、抢单、撮合、通知等都完全不同。

这样的设计在重构上线后,业务系统大框架在两年时间基本没有动过,最多是增加为一些新的指标或者函数进行开发,但不会影响主流程,业务系统的架构非常稳定。

市场部门或是业务部门要上线一个新的运营策略,比如一号专车在2015年和Uber打战的时候,上线一号快车,在研发部门实际只用了两天就已经配置测试完毕,为了配合运营的工作,才拖了一周才上线。

限于篇幅因素,技术细节这里就不再多说,给大家看一个最小片段的机场单的产品命中规则

( 单v城市 ==1 ) && ( 函v电子围栏 (单v目的地,上海浦东机场) ) && ( 单v渠道 ==0 || 单v渠道 ==1 || 单v渠道 ==201 ) && ( 单v类型 ==2 )&&( 单v入口 == 0 )&& !((单v用车时长>=1)&&(客v版本号>=4))&&(单v回调码==0)

题外话:在一次偶然的机会,和行业内的各家专车系统对标时,发现一号专车这种系统设计和Uber的设计思路非常类似,所以Uber才可以让各城市经理在不同城市开通不同车型做各种各样的活动,Uber系统应该是经历了数年演化,而一号系统重构头尾只花了2个多月,实际上一号的指标维度非常多,有些甚至用自定义函数来组合使用一个指标,因为国内的运营策略变化实在太快太多了。殊途同归吧。

最后再说到时间、时间….时间啊!上面提的许多技术问题,如果从时间的维度上考虑,根本就不是问题,什么GPS距离计算算法不准、去噪及道路拟合算法不够优、流控还没有做、长连接服务质量不好、SQL慢查优化,给我们时间,统统都不是问题。重构计划是4月9号开始, 我们在阅读旧有代码、梳理业务、设计新的数据库结构、搭建团队、搭API接口层框架、搭分布式服务架构(Thrift + ZK + DHF SRV Framework + Chukwa[实际没用起来])的过程中,很快就到了5月1号。

劳动节后,第一波炸弹来袭,听说其他几家打车应用也在做专车,天下武功,唯快不破,我们必须最先上线,于是计划调整改到7.15上线,下了死命令,好吧,干吧,需求不能变,时间变了! 临近5月中旬,新的需求来了,要求我们直接和快的打车端对接,至少要在重构上线后两周内也上,没有办法,在打车领域竞争的激烈程度超出所有的想象,业务发展的速度都是按天算的。

于是我们把上线时间又提前到6.15,并计划6.30上线快的APP端的一号专车,时间,还是时间,需要提前梳理对接流程、接口定义及开发,人呢?时间呢?好吧,继续干吧!没有抱怨,也不谈梦想、更没有什么改造世界的想法,仅仅是一份信任、一个承诺,整个重构团队日夜加班,从4月9号计划启动,到6月18号上线,连续70天没有休息一天,前后端的重构团队都是全公司来得最早走得最晚的人,终于还是让系统上了线。PS:苦过笑过,留下的都是许多欢乐回忆,当值得书写留念。

上线前一天

上线前一天,虽然已经预演过两次,但大家心里都没有底,因为专车业务量刚刚起来,系统如果真的出问题,将会给对手们一个绝佳的机会。内部在讨论的时候,问的最多的一个问题是如果系统挂了,我们有什么预案,做了两套,一套是切回老系统,但会出现新旧数据不一致的问题,后续补数据非常麻烦;再一套是需要运营的兄弟们支持,系统只记录下单信息,然后出后台报表,由运营同学手工派单,再打电话通知乘客和司机接送,回到原始社会,这就是现实。

通宵不眠,迁移当天增量数据、服务上线、回归验证、强制升级,深更半夜,运营同事们也开着车在路上晃荡着帮忙路测,悦耳的新订单铃声,成单播报不停响起,终于熬过去了。

系统上线:快乐并痛着

系统刚上线,基本的业务流程虽然没有出什么大问题,但随着订单量的迅猛增长,迅速开城,业务变化,各种状况层出不穷:

  • 长连接QoS没有做好,订单撮合成功,但司机或乘客却没有收到推送通知;

  • 司机行驶距离根据GPS+WIFI+基站三种制式的GPS坐标值,去噪算法做得不够好,计算出来的距离差异巨大;

  • 定位不准,司机有意或无意没有开GPS、网络,导致发单距离太远;

  • 订单量上来,索引不够优化,慢查SQL层出不穷,数据库CPU百分百;

  • 当时上的是阿里云,阿里云的数据库专家在监控应用的时候,曾经给过一份数据库的诊断报告,非常客观的将我们数据库的问题类比成某宝08、09年的状态,惭愧;

  • 代码没有Review,小分支的逻辑测试不到位,出现死循环,拖垮系统;

  • 当日订单超过10W的时候,司机做单时上报的心跳对后台存储(MongoDB、数据库)压力过大;

  • 因时间问题,日志系统没有上线,在跨服务的应用中,无法实时监控各服务运行异常并及时告警,为此付出很大代价;

  • Redis在日订单过百万后,也扛不住压力了;

一方面前方捷报不断,另一方面后方销烟弥漫,快乐并痛着,不停的发布新版本,修复BUG,优化系统,出现过好几次系统大规模宕机,大部分都是因为各种因素(不仅限于慢查)导致数据库不堪重负引起连锁反应,在订单过百万之后更是连续一周的时间,在每天的晚下班高峰期系统负载扛不住,当然后来在前后端团队通力合作下,都顺利优化顶过去了,大致回想一下,挑一些简单的列一下:

  • 优化道路距离优化算法;PS:高德地图的兄弟给了许多帮助;

  • 所有道路距离计算只取GPS,基站及WIFI获取的点不做为道路计算使用,但听单可以使用WIFI的GPS点;

  • 优化寻找周边司机算法;

  • 提升长连接的通讯服务质量,在移动环境下网络不稳,需要双向确认及心跳包客户端打包机制;

  • 服务继续拆分;

  • 数据库分库、分表、一主多从、读写分离;

  • 多级缓存,优化缓存的使用,数据库只做存储,如Redis Sharding分Memory和Persistence两类等;

  • 设计了两层的限流熔断方案、服务降级策略等;

  • 旧的SQL全量Review,新SQL必须DBA确认后方可上线;

  • APP端也优化了访问策略;

  • 加上日志监控系统,监控系统整体运行状况;Ps:感谢快的兄弟们的支持;

  • 增加服务器,这一点我们做得不错,使用了不到一百台的4核*2.8G CPU,16G内存的服务节点,就挺过了数百万单的业务,且每个服务结点的资源消耗都在个位数;

还有很多细节已经回忆不起来,欢迎大家后续多交流。

一号专车的重构其实是重写,在业务不停往前奔跑的过程中,重写是下策,风险很高,要慎而又慎,好在当时的订单量还不大。大部分时候,系统重构应该尽量要在充分了解业务的基础上,采用分而治之,分阶段进步的方式来,开着飞机换引擎还好,但我见过开着飞机换飞机的重构计划,着实为对方捏一把冷汗,不知道最后结果如何了?当然,如果决定要动手,那还是越早做越好了。

后序

以上行文仅做为系统重构的技术回顾,重构过程艰难而痛苦,它不仅限于技术层面的难度。此后的过程中系统挺过了一轮又一轮浪峰的冲击,也逐渐趋于稳定,基本上算是完成了组织交给我的任务。

2015年底,Joe开始了他的新项目——四叶草车险,开始了另外一次从0到1的挑战之旅,他再次邀请了我,我于2015年12月31日离开滴滴快的,加入四叶草车险平台参与互联网保险的创业项目。

这一次挑战更大,保险业在中国是一个万亿级的市场。据了解,国内的比较大的传统保险公司,一家公司往往都有三、四百个系统在支撑业务运转。保险未来的变化极其复杂多样,我们才刚刚开始,未来还要迎接诸多挑战,在这个过程中,我们需要更多的人才加入,来和我们一起体验新的重构之痛(le)。说句老套的话,我们可以提供业界有竞争力的薪资以及和团队一起战斗成长的经验。

一号专车接口文件

  • 2015年05月15日 11:26
  • 1.45MB
  • 下载

igrimaceV8.0.0 IG 一键新机 陌陌 Uber优步打针 平安易贷 滴滴 一号专车 饿了么 ios8 V8 ZTN(插件安装方式)

iGrimaceV8.0.0安装步骤 1、系统必须为 IOS8版本,手机需要越狱 2、打开cydia 选开发者 下面点管理 软件源 点右上角编辑 添加源 apt.178.com 搜索 apple Fi...

igrimaceV8.0.0 IG 一键新机 陌陌 Uber优步打针 平安易贷 滴滴 一号专车 饿了么 ios8 V8 ZTN(插件安装方式)

iGrimaceV8.0.0安装步骤 1、系统必须为 IOS8版本,手机需要越狱 2、打开cydia 选开发者 下面点管理 软件源 点右上角编辑 添加源 apt.178.com 搜索 appl...

igrimaceV8.0.0 IG 一键新机 陌陌 Uber优步打针 平安易贷 滴滴 一号专车 饿了么 ios8 V8 ZTN(插件安装方式)

iGrimaceV8.0.0安装步骤 1、系统必须为 IOS8版本,手机需要越狱 2、打开cydia 选开发者 下面点管理 软件源 点右上角编辑 添加源 apt.178.com 搜索 apple Fi...

iGrimace IG 3.0 VX v3 iOS神器 新机 抹机 优步Uber 陌陌 微信 携程 同城旅游 美团 大众 一号专车

纯手机安装可以添加源apt.so/q1293585419  添加后,下载安装vx,即可,需要,Z spirit加强版的,安装 Z spirit即可 iGrimace安装步骤 1、系统必...

Android开发:仿照一号专车的地图页面

之前有使用过一号专车,觉得地图模块很好用,于是自己也模仿的做了一个,用的也是高德地图 Gif效果图很模糊,你也可以用手机扫描二维码下载APK体验,完整的代码我放在了页面的底部,在博客这里,我展示些比较...

igrimace v3 做uber优步教程 陌陌解封 神州专车 快的打车v3 一号专车 陌陌站街 vx

1 主界面设置   这里注意设置如图所示设置 (参数记录小黑点点选了,打开参数记录会看到伪造的历史记录,对应点入,会相应的进入历史伪造参数同时抹除设备选取软件信息!) 2增强插件设置   随机地理位...

代码帝:一个月10万行代码 (只为存着膜拜)

我所知道的一般程序员的代码效率在一个月 2K到5K之间吧!我所知道的,具体多少根据实现语言不同会有差异。这是统计出来的数据,当然不是不可能更高的代码效率,只是那样会导致 bug数量的成倍增加,当然还有...

智芯一号代码

  • 2014年10月07日 10:30
  • 2KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:50天10万行代码,一号专车系统重构细节回顾
举报原因:
原因补充:

(最多只允许输入30个字)