OFC的重要性
2014年的618显得和以往任何店庆促销日都不同,不仅仅是因为电子商务本身在中国不断飞速发展对京东系统带来的挑战,更为重要的是2014年5月22日刚走入美国纳斯达克殿堂的京东聚集了最耀眼的光芒,能不能保持这样的光芒,618则会是一份很有说服力的答卷,当然我们最终给出了满意的结果。作为一个普通的购物者,当我们在浏览器中输入并回车,便可以看到京东商城的首页,根据自己的需要选择喜欢的商品,然后加入购物车,提交订单后,即可享受京东的极速物流体验,最终完成一次简单快乐的购物历程。其实,订单提交后,需要经历多个环节和各个系统的处理才能完成使命。其中有一个环节是订单必须经过的,而且这个环节连接了用户下单和订单在库房的生产,就是订单履约中心(OFC,Order Fulfillment Center),本章我们就为各位解密这个部门。
2014年的618后,京东技术团队分享了如何应对店庆日及以往促销活动在技术方面的经验和探索。其中有一讲“电商海量订单处理OFC系统的关键技术环节”(见京东技术开放日第一期),说的就是这个部门做的事情。
这个部门的职责,用彭青的话讲就是,转换用户订单为各终端系统的生产单,并且按要求送达到相应终端系统。举个例子,就好比我们将采集到的原始食材按照客户的不同口味(不同系统)进行烹制,并且在指定的时间内做好后送到客人(终端系统)那里,整个过程包括订单的拆分转移和订单的下传。其实我们从网站下的订单(也叫原始单)在库房是直接生产不了的,需要经过OFC这个环节的处理后,才能到达各个生产系统。由此可见,这个环节必然会有大量数据需要处理,而且还要保证时间。
想必大家知道关于京东的211、311等配送方式,如果用户选择不同的配送时间,京东的快递就必须在用户指定的时间送达,而如果下游的生产系统收到订单数据比较晚,就会影响后续的配送时间,最终会影响客户体验。现在订单下传,对接的全国库房近150个,需要调用的外部处理订单服务也有近20个,而每个系统的处理能力和响应能力又各不同,这就需要我们进行相应的调节流量的配置,这其中只要有一个系统存在问题,就可能会影响订单的下传。
OFC的形成
2003年京东网站创立之后,迎来全国电商的快速发展,京东的业务随之不断增加,导致相应的业务系统也在持续增加。直到2011年,随着系统增多及业务的需要,逐渐成长出来一个小团队,负责在各个系统之间进行数据的传输,将客户订单数据传到库房,同时需要将非客户订单,如采购单、供应商、内配单等二十多个业务传输到相应的业务系统,至此OFC成形。
初期由于各个系统的不完善,导致数据传输总是存在各种各样的问题,订单总会卡在这一环节,而作为传输环节又必须保证数据能正确传输完毕,这就需要我们必须了解上下游系统的业务及数据处理的过程,只有这样才能知道问题产生的原因,才能知道该如何去处理问题。但是由于上下游的系统是需要经常上线新需求的,我们每天需要及时了解上下游系统业务的处理,才能保证我们的环节没有问题。
那段时间兄弟们真的很辛苦,每天需要处理上千个工单,加班更是常事,以至于一个同事说睡梦中都在处理问题单。由于业务领域划分存在问题,系统边界不明确,导致出现许多莫名其妙的问题,兄弟们干得很苦很累,直到彭青来了之后。彭青带着一个厚厚的黑色全框眼镜,个子不算太高却有个小肚腩,学生族的发型让人感觉很亲切。彭青根据他多年的工作经验和对系统的理解,对这块业务进行重新划分,逐渐将非客户工单的数据传输工作交接给对应系统进行处理,将兄弟们解放出来,开始客户订单处理的新征程。
到2011年底的时候,在彭青的带领下我们已经成为了二十多人的团队。为了更进一步拓展在客户订单方面的业务领域,我们主动接手了订单拆分系统和订单转移系统。这两套元老级系统是用.Net编写的,由于前辈们大都不在职了,文档也不完善,对于系统内部业务了解的人很少,修改非常难。而此时每天由于系统问题导致的事件单多达上百,也就是说每天运维带来的工作量都是可观的,在这样的情况下,接手这两个系统自然全无阻力。
技术的改造.Net到Java
系统接过来了,第一步要做的事情当然是重写。对技术的选取,根据当时公司技术发展战略以及Java的普及,我们选用了Java。重写过程中需要梳理已有的业务,自然少不了不断和原来系统的人员进行交流,去确认业务流程和技术处理细节。经过一个多月,系统的重写总算完成,接下来的工作就是上线了。
开始小流量地切,我们通过开关进行控制,通过省市县区域分流,到2012年2月系统算是上线了,而之前的.Net系统也逐渐退休了。到这时候,OFC逐渐根据业务划分为3块,第一块是订单拆分,第二块是订单的转移,第三块就是我们前面提到的订单下传和回传。
这里要给大家解释一下:
订单拆分,可能大家想到了,就是将用户下的订单,根据我们库房的不同分布,比如大家电仓、百货仓等进行拆分。当然这是拆分最初的也是最基本的理解,到后面会给大家讲讲现在的拆分是什么样子。订单的转移则是根据拆分完的订单以及库存等属性,将订单转移到下游的系统。订单下传和回传主要是指,转移之后的订单调用库房相关的预分解、打包等服务环节,进行订单下传和生产,当订单顺达客户手里,还需要将订单的相关数据进行回传。当然这里只是粗略介绍,后面我们会更详细地给大家呈现。
211订单履约率提升项目
重写完之后,系统总算可以正常运转了,而接下来的事情就是对系统的进一步梳理和优化,以更好地支持将来业务需求的发展以及技术方面的扩展。当然有的时候系统的改进往往是由于外部业务的无法适应导致的,这也符合变革的本质。用户体验至上一直是每一位京东人追求的目标,就连我们的老刘也会隔三差五在网上下单来体验一下,而这次老刘发现了一些问题。当他下单后,等了半天才收到订单,这让老刘无法忍受。经过调查后发现,从下单到库房竟然花了两个多小时。
改造前的系统整体设计图
于是老刘立即成立了一个叫作“211订单履约率提升”的项目,该项目涉及11个系统的升级改造,包括订单交易系统、订单管道系统、拆分系统、转移系统、订单任务系统、OFC相关系统、预分拣系统、面单系统、增值税资质服务、发票系统、WMS系统。其中4个系统需要全面改造,3个系统需要大量改造,剩下的4个需要少量改造,而且由于与订单相关的业务点多且逻辑复杂,无法在测试环境下全面测试。这不仅影响着整个订单的正常生产,甚至会影响财务相关业务。项目任务非常重要,要求两个月内保证订单从下单到库房的时间缩短到5分钟内。大家马上开始了工作——需求讨论6天,设计方案5天,开发15天,功能测试20天,性能测试44天,上线部署调试26天,总计工时达5066小时,最终实现了项目目标。与此同时订单下传各环节的服务性能指标也得到了规范,使得订单下传趋于稳定,理顺了订单下传流程。技术方面也得到了锻炼,使用了Zookeeper分布式配置、CXF Timeout设置、Log4j多Tomcat示例配置、Oracle数据库分区转历史方案,数据库使用了OracleExadata、MySQL。在这过程中和Oracle技术团队直接沟通多达10次,在数据库设计方面、性能调优、转历史数据方面都得到了提升。更为重要的是锻炼了团队,对于战胜艰巨任务有了更大的信心。下面是系统的整体设计图。
改造后的系统整体设计图
对于拆分,在几位大牛对系统的业务进行梳理后,发现部分业务有些混乱,业务领域划分得不是很清楚,拆分系统中除了需要根据商品的不同属性进行拆分外,还需要对订单中使用的金额、优惠、运费等信息进行分摊处理。这几位大牛敏锐地发现系统这样设计是有问题的,于是就把金额信息处理逻辑拿出来,专门做成一个服务——OCS订单金额计算服务(OCS),拆分只需要对其调用就可以。同时,我们对OCS分摊结果的数据进行了持久化数据存储。系统这样设计,不仅解耦了拆分服务之前混乱的业务处理逻辑,OCS的数据也一举填补了公司这方面数据的空白,成为其他系统使用和处理业务逻辑的数据基础来源。到现在为止,直接使用OCS数据的系统就有二十多个,其重要性不言而喻。
SOP合页单项目
2013年,公司级项目SOP合页单要启动,即用户购物车里既有京东自营的商品同时有POP商家的商品(SOP)。在结算的时候只需要提交一次(之前只能分开提交,类似淘宝多商家的商品只能单独提交)。为了改善用户体验,同时需要将提交之后拆分完的子单结果显示出来,需要我们团队提供一个拆分服务供交易组使用,这是一个重大的考验。下单环节的速度非常快,TP99一般都是几十毫秒,而我们目前的服务则是几十秒,完全不在一个数量级。为了保证项目能够顺利完成,我们既需要满足日常的业务需求,同时要新切出一个分支进行修改,用于此次项目,同时需要将针对新需求的代码及时同步到这两个分支上,任务非常艰巨。解决了开发问题后,就要想着如何在性能上有所提升,比如,可以放在内存里处理的就放在内存中操作;尽量减少对外部服务的依赖;对于非同步化的操作进行异步化;对于部分服务我们甚至采用降级的方式,在必要时通过开关进行降级,保证整个服务的整体性能。如此这般后,我们主动要求性能测试组对我们的服务进行性能测试,在代码级别进行了优化,最后在指定的时间内成功地完成项目。而此时我们在维护着同样级别的3份拆分服务代码,老的下单对应的我们前面说的老拆分,新的下单对应的我们新的拆分,还有我们为交易系统提供的预拆分。
而在此时最困扰我们的不是维护这些系统,而是经常会由于网络不好,使一个订单的服务超时,进而导致服务进行重试,而事实上订单已经提交成功。这就可能使我们错误地提交两次甚至是多次订单,比如客户下一个原始单,需要拆分成两单,但是由于上述原因可能会得到多单;如果用户选择货到付款,会给用户造成困扰,会带来配送的成本,如果是在线支付的话则会导致公司的损失。刚开始的时候没有解决方案,只能通过监控去发现,发现后人为锁定这些订单,而这样不但增加了运维压力,而且人工处理难免会有失误。由于我们在提交子单之前会获取订单号,每一次获取的订单号都是新的,这会导致调用这个服务时对订单号是无法防重的。后来海波想到一个防重方案,就是我们在调用这个服务之前将订单号信息输入自己的防重库,新订单来的时候先在防重库中进行查储,如果有订单信息则说明之前提交过,本次提交失败,然后直接把库里相同订单号的数据拿出来提交即可,这样还可以节省订单号。如果库里没有查到,我们将该订单号插入库中,同时调用服务。问题得到有效解决。本次提交经过这一系列的处理优化,系统总算是比较稳定了。
转移架构升级
转移系统也进行了大规模的调整,为了进一步保证订单及时准确地转移到下游的库房系统,转移团队在业务和技术架构上进行了一系列的改进:业务和数据处理异步化,即将可以异步化处理的业务和数据放入分布式队列,由对应的模块处理;使主流程业务简单快速流转;数据处理并行化,将数据切割成多个业务单元,并行处理业务单元;针对变化少、实时性要求不严格的热点数据,使用缓存并配以更新机制,以提高性能;对于业务洪峰,通过平滑控制保护后续系统不被洪峰压垮。
在业务流程方面也进行了优化。由于涉及订单生产流程,需求变化速度非常快,需要不断梳理现有流程,去除不必要的流程,减少有新需求时对不必要业务流程和分支的考虑。同时,需要对现有分散业务不断地抽象和改造,以方便业务扩展。
面对这么多的优化和改进,每次上线的风险无疑是巨大的,如何规避风险呢?那就是要分流、可配置化,以及运营工具先行。由于新上线的项目风险较高,特别是容易忽视一些对外交互的小功能,而发生线上问题时又无法及时切换。因此,需要对业务上线进行分流,并且通过灵活便捷的配置中心随时进行控制。对于异常情况一定要优先考虑,并且开发相应运营工具,以备紧急情况使用。尤其不能抱有侥幸心理,认为小概率事件不会发生在自己身上。
转移团队的负责人铁总(大家总是这样称呼他)已经从事电商十余年了。这个来自湘西的汉子对待工作总是严肃认真,但面对生活却又充满热情;结婚前总会泡在游戏中,或者痴迷羽毛球,女儿出生后便成为了他的一切。在谈到转移未来的规划和发展时,他充满自信地说:“将来会在保证客户体验的同时,更多地通过在成本和流程上优化来降低成本。库存分配将在保证订单履约的前提下,打破现在先下单先占库存的规则,提高商品库存周转率和现货率,同时给客户提供更早的收货时间选择”。
转移系统整体流程图