京东服务市场是为第三方软件服务商和京东开放商家提供的交易服务平台,为第三方服务商赋能,并为其搭建起与商家间交易合作的桥梁。
服务市场团队在2018年完成了云平台京东服务市场的交接与POP平台京麦插件市场的系统融合,并承载着京东自营与三方越来越多服务进行商业变现的业务诉求。相对于传统的电商系统,服务市场面对着的是更复杂的业务领域,更灵活多变的交易组合场景,如何让系统具备积木式赋能的能力,通过松耦合架构设计具有系统高内聚性,把耦合度降到最低,进一步实现较为合理的微服务架构风格的应用系统,期间遇到了很大的挑战也总结了很多经验。本次分享和大家回顾这个艰辛却有收获的过程。
2017年底,京东云平台京东服务市场交接到POP平台京麦插件市场进行系统融合,我们称之为两市融合。两市融合是为了实现对外统一的服务入口,实现两个系统变成一个系统,但当时这两个市场却是两套完整的系统闭环。
融合项目是对两个市场进行深度的融合改造,融合不仅是进行流程融合和数据融合,其中最难的就是同时支持双边业务的并行运行,所以,整个融合过程在服务发布、服务审核、服务展示、服务订购和服务使用等环节对两个系统原业务进行了大量的兼容处理,造成了极高的系统复杂度和极差的稳定性。
融合方案并没有采取单边市场下线的方案,而是采取了将两个市场裁取缝合的处理方式,交易流程使用了京麦插件市场的流程,商品流程使用了服务市场的流程,这种融合方案现在看来的确不明智。
并行业务的难度在于双边读业务与写业务的同时支持,因为不能简单的下线一边的业务,但当时对数据的读写入口不能完全梳理清晰,所以融合过程中对上下游各种业务需要兼容支撑,因此在融合中的过渡阶段产生了三套流程并行的尴尬阶段,并且还需要同时双写三套数据库表及同步多个数据缓存,复杂度可见一斑。
当时两个市场的两个数据库是独立对外提供数据服务的,如何将两个数据库变成一个数据库,是当时最大的难点。
而且服务市场作为一个交易系统,数据库是系统的核心,不仅MySQL有很多从库,还有Redis、ES、Solr、HBase等作为数据缓存,所以在数据异构方面做了很多功夫。
流程融合包括服务发布审核流程融合、评价评分流程融合、服务搜索流程融合、订单订购流程融合、结算流程融合、退款流程融合、取消订单流程融合和发票流程融合等等。
以订单订购流程融合为例,由于服务市场和插件市场融合切换是无缝的(不能停服务),且两侧订购、订单数据结构又不完全一致,同时数据切流又是逐步放量。所以订购、订单流程的融合采用数据库双写的方式,所谓双写方式,是在写原服务市场数据库和插件市场数据库时,通过订阅Binlog,使用BinLake框架订阅写入SQL再整理数据写入新数据库。最终所有服务通过调用新库进行查询服务。
这种双写机制可以很好的进行回滚,当新流程出现问题的时候,可以切回到老数据库进行降级。当新订购流程出现问题时,也可以通过关闭切流控制影响范围,采用老流程进行订购订单处理流程。
这里补充说下,插件市场和服务市场的老流程都是写到各自的老数据库中,而新流程其实也是写到服务市场的老数据库中,新数据库的数据都是通过BinLake写入的,这样保证在流程融合过程中的数据一致性。
而流程融合完成后,即读入口都切到新数据后,就可以逐步切换写入口了,把两套数据库变成一套数据库。切换的方式其实很简单,主要是通过配置中心进行逐步切换写流程,但要考虑切换失败如何处理异常流,假如遗漏了一个场景,还有业务读老数据库,但这时老数据库里是没有数据的,怎么办?
所以,准备了两套方案,一套回滚,另一套补偿。考虑到回滚的操作难度很大,因为代码回退、流程回滚可能出现的风险会很高,因为那时数据已经不一致了,所以首选方式是补偿,当数据出现不一致时,启动后端校验Worker将数据补偿到老数据库中,并迅速修复线上问题。
随着融合的深入,服务市场的架构逐步演进成为经典的三层架构,底部是外部依赖,概括主要是京东商城和京东金融的中台服务;中间是服务市场的服务层,包括服务引擎、商品中台、订购履约中台和支付中心等系统;上部是服务市场的应用层,包括服务市场大前端、商家工作台、服务商后台等系统。
本着 SOA 微服务化的架构设计理念,将服务市场设计为模块化和层次化的架构风格,高层次模块调用低层次模块,低层次模块通过接口向上提供服务,以实现系统之间的解耦。但是对模块的设计粒度拆分的过细,这使得随着业务的发展,整个系统变得愈加复杂,系统之间的业务调用关系也变得愈加混乱。
复杂的调用链路,必然造成业务之间的相互影响,所以,通过对服务市场从部署、数据库访问、服务PRC调用、消息接收等进行了纵向垂直部署隔离,分为商智、京麦、服务市场等多套隔离部署层,实现即使任一垂直域无论因为服务器还是数据库问题,影响不会扩散到其他业务上。
复杂的架构,不仅造成新需求消化越来越慢,而且系统也愈加的不稳定起来,这在未来的发展过程中,效率和成本都将成为很大的问题。所以,痛定思痛,决定进行简化系统架构,将系统设计的越简单越好,链路越短越好,流程越简单越好。因为‘A调B’一定比‘A调B调C调D’的链路要简单,而且出现问题更易于排查。
所以,我们进行了逆向微服务化的架构改造,即系统的整合,确立了三个中心和一个引擎的系统架构,且是多层次的服务对外提供服务,而不是由一个中心层统一对外,要去中心化,微服务化的架构设计思想也是去中心化,即每个中心都可对外提供服务,因为层次降低和纬度降低之后,就非常容易定位问题。
系统复杂度等于系统个数加上系统之间的调用链路个数,相比简化之后的架构,可见系统复杂度降低了多少。而且还在业务侧逐步改造、迁移、下线一些老业务服务或功能,这相当于给系统做了瘦身,将无效代码都清理掉了。
经过改造后的服务市场,由于系统进行了整合,系统变少了,虽然系统的隔离防腐在部署上看似没有什么变化,但每个系统横跨的业务层是变多了的,我个人认为这其实是就是组件化的方式,就是一个系统可以复用给多个业务场景。
随着两市融合的终结,流程融合和数据融合的完毕,系统实现了统一的服务层,这使得对外提供服务的难度降低很多,否则一个服务在多个系统提供相似的功能,这出了问题是很难快速排查的,所以融合不彻底是无法实现服务化的,服务化也要求必须统一入口、统一流程化。
所谓系统架构的终极思想,就是降低软件复杂度,不是把系统做的越来复杂,而是把系统复杂度做的越来越低。
当系统实现服务化之后,单品页就不会三千多行的代码的业务逻辑,还调用了很多接口,而是通过服务化在Action层进行服务接口的整合。同时,也可以灵活的实现各个服务的降级处理,以提高系统的稳定性。
MySQL作为当今主流的数据库,业务中如果使用的好的话,决定能扛住上百万,甚至上千万的量,但是现在大多数系统做不到,这种情况下就要考虑如何做到MySQL防御。服务市场是一个交易系统,如果把库打挂了,那将造成系统不能生产,业务不能收钱,这是非常严重的线上事故。
早期的缓存架构设计是JVM-Redis作为热点缓存,但是在冷启动情况下,可能会出现缓存穿透,造成MySQL出现很大压力。所以,后期设计完全避免这种穿透的可能,Redis完全作为数据存储,与MySQL进行隔离,Redis查询不到数据,就直接返回,与MySQL的数据一致性通过数据异构来实现。
服务市场使用BinLake进行数据异构,即通过订阅MySQL的Binlog日志,通过接收JMQ进行数据异地构建存储。
数据异构主要有两种方式,一种是顺序消费、另一种是并行消费。其中,在进行订单、订购的数据异构时是要求保证严格的顺序性的,因为并行消费是无法保证订单的先后顺序的,所以可能造成数据不一致。
但顺序消息的问题主要是单点消费效率慢的问题,以及消费出了问题就会造成阻塞,之前使用服务器进行消费,通过IP限制保证单点,后期切换到流式计算平台(Storm/Flink)进行处理,流式计算在并行写ES和JIMDB有天然的优势,但如果异常情况下出现写操作失败,对于JMQ的重试系统要做好幂等操作的处理。
而商品数据异构是使用并发消费的,因为商品数据不需要保证严格的顺序,但商品数据异构的问题,是一条商品数据在MySQL中是存在多张表中,而异构到ES或JIMDB中就是一条记录,那对于商品数据是订阅那哪些表的Binlog呢?
所以,我们的实践中商品数据异构是订阅商品的主表,其他信息通过反查数据库获取的。但这个场景下,并发消息虽然会提高处理效率,但由于反查数据库会造成额外的IO开销,而且反查数据库还是主库,因为BinLake和数据库主从同步都是基于Binlog,这两者之间的速度是无法保证谁先谁后的。
最后,考虑系统谁也无法保证不出问题,所以还使用定时任务在每天一个时间点进行数据一致性的校验。
在过去的一年里,不仅服务市场的系统架构发生了巨大的改变。在融合之后,思考的重心也逐渐变成了如何将越来越多的服务接入到服务市场上进行售卖,所以,工作上,一是对服务市场已售卖的服务进行搜索优化、对先后端类目进行解耦、优化排行等,二是丰富服务市场业务场景覆盖度,快速接入多样服务入驻服务市场。
并通过拆解业务、抽象规则、能力下沉,构建积木式的交易服务平台,实现快速接入官方&三方服务,赋能开普勒、无界零售、7Fresh、国际站等多个业务场景。
服务市场通过服务交易能力提升项目,实现了服务交易流程配置化,将早期入驻服务市场时间需要花费2~4周减少到0天。
所谓交易管道,简单说,就是当有一个订单来了,通过一个流程从订购、订单、支付、台账、发票、退款等节点往下走,这就是一个管道。但是,如果每有一类服务,就做一个流程,如SaaS一套流程、质检一套流程、ERP一套流程等,那最终只能通过堆人的方式进行开发,而这种开发效率也是极低的。
其实,通过对业务不断的沉淀,你会发现大多数服务流程都是有很多相似性的,唯一不同的就是之间的配置,那我们把这个可以复用的功能提炼出来,称为配置中心,用工作流的方式驱动这些数据在管道中进行流转,这就是流程配置化。
配置中心采用多级缓存、数据库拖底的方式来保障交易参数获取效率和高可用,所以,在整个交易的全生命周期里,都会依赖于配置中心,基于配置中心的参数进行流转。
所谓商品模版化,就是对商品的属性进行纬度化和属性化。
参考京东商城的服务发布流程,一个实体商品的发布信息,主要包括商品信息、商品属性、图片、简介,及最重要的销售属性。一个商品可以认为是一个SPU,而SKU是通过多个销售属性组合而成的,一个SKU才会确定唯一的价格和库存。
基于模版引擎,在规则上进行剥离,在服务属性低耦合下,可以通过模版的方式自定义一些服务的发布信息和规则,并实现属性与履约规则的配置关联,作用于交易和订购履约中。
服务市场发布的SaaS服务都是需要进行履约的,履约的方式主要有周期+版本、周期+模块、数量+模块等,其中版本与模块的区别在于订购时升级与续费的逻辑不同。那发布服务的销售属性如何与履约规则进行配置关联,我们比较了三套方案,最终使用的是方案三。
最后,希望将京东服务市场打造成为精品的交易服务平台,做到服务市场更加稳定、交易流程更加便捷、接入服务更加高效。
同时,服务市场团队也在不断壮大,新思想的碰撞,有好的方面、也有坏的方面。我希望用自己的行动,不断影响团队,让团队变得更有凝聚力、更有战斗力。
----------------------
作者:张松然,京东商城商家研发部架构师。丰富的构建高性能高可用大规模分布式系统的研发、架构经验。2013 年加入京东,目前负责京麦服务网关的系统研发工作。