京东京麦交易平台的化繁为简之路
您目前处于:技术核心竞争力  2018-01-02

前言

京麦是服务于京东数十万商家的开放式工作平台,由京东官方和ISV(第三方服务商)通过京麦为商家提供多样的应用服务。


京麦交易平台简介

京麦交易平台是整个京麦的核心系统之一,通过整合京东内部的各个系统的能力来打造一个功能丰富、稳定可靠的通用虚拟服务交易系统,我们首先来看一下整个京麦交易平台的生存环境:



京麦交易平台是一个通用的交易系统,具备对外开放的能力,所以很多京东内部的系统都是通过京麦交易平台来实现他们的交易能力,而京麦交易平台则通过整合京东内部各个子业务系统的服务能力来提供这样一个丰富的交易能力。

可以看到,整个京麦交易平台的生存环境是非常复杂的,并且业务增长飞快、变动频繁,下面将通过几个例子来介绍整个京麦交易平台是如何在一个如此复杂的环境中稳定的运行,同时要具备良好的业务扩展能力。


交易流程的高性能设计

对于一个交易系统来说最重要的当然就是交易流程,上面我们看到整个京麦交易平台依赖了非常多的外部系统,这对于交易流程的性能是一个非常严峻的考研,频繁与外部系统进行交互必然会产生大量的网络耗时导致性能的降低,并且我们也无法保证我们依赖的每一个系统都能为我们提供高性能、高可用的服务,下图是我们整个交易流程设计的核心思想:

整个设计的核心思想就是缓存和异步化,我们会对商品、用户等热点数据进行多级缓存,减少RPC接口的调用和数据库的访问,同时我们会梳理出整个交易流程最核心的一条线,除了这条线以外的分支逻辑通通异步化,这也是系统异步化设计的一个理念,在图中有一个异步设计的例子,在生成订单的时候其实仅仅是生成一个订单id,而真正订单写库落库的操作是异步完成的,这样可以减少访问数据库的耗时。缓存+异步化的设计可以保证我们核心交易流程的高性能。


履约流程的高扩展性设计

用户在京麦交易平台完成一次交易之后,我们要对用户的本次交易进行履约,例如我们要记录本次用户订购的服务让用户能够使用他购买的服务、要发消息给用户和ISV、要推送本次交易的结算信息到结算系统等等,整个履约流程的步骤非常多,并且会频繁的增加,所以我们要保证每个步骤之间不能相互影响,要保证整个流程良好的健壮性,并且需要高扩展性来应对业务的增加。

这样图阐述了我们整个履约流程的设计理念 — 观察者模式,履约流程的每个步骤都抽象成一个观察者,它们同时监听同一个订单消息主题,各自完全没有影响,并且具有非常好的扩展性,后续针对业务的扩展我们只需要增加观察者即可,不会对原履约流程有任何的入侵,这样也可以保证整个流程的高容错性。

相关内容已经整理到博客中,详见 观察者模式在交易系统中的应用


复杂业务的智慧处理 — 引导路由

用户在京麦交易进行一次交易的时候,我们需要为用户的这次交易选出合适的支付方式,这个流程在业界有一个通用的概念,就是引导路由,而影响一次支付方式选择的因素有很多,例如每个接入系统、订单类型、用户账号类型、交易限额、商品信息等等,这些因素都会有不同的支付方式的支持,这些因素相互作用在一起选择出本次交易到底支持哪些支付方式,这个逻辑非常复杂,而且未来会有更多的因素影响支付方式的选择。如何简化引导路由逻辑复杂度的同时又要保证它的高扩展性,成为我们的一个难题。

这张图就是我们解决这个难题的答案,首先我们会对影响因素中灵活异变的因素做灵活的配置,采用zookeeper订阅、通知的能力来完成配置下发的功能,其次我们会把每一个影响因素抽象成支付方式这一主题的装饰者,用装饰者模式来将这些因素相互作用在一起,这样的设计可以应对业务逻辑频繁的变动,并且可以保证良好的扩展性,整个引导路由的逻辑复杂度也会降低。


发票模块的高扩展性设计

发票模块是京麦交易平台的核心模块,依托于京东发票系统实现,京东发票的出口有很多,例如有通用发票、融聚发票,针对发票类型还会有增票、普票等不同发票类型的出口,未来我们还会扩展泰国和印尼的业务,也同样是不同的发票出口,当用户开具发票时,我们要在众多发票出口中选择出唯一的一个出口来完成发票申请单的提交,同样有很多的影响因素,例如接入系统、账号类型、订单类型、商品、发票类型等等,并且出口还在不断的增加,针对这种场景,我们决定采用责任链模式来解决,如下图:

首先构建发票申请的责任链,每个出口都会成为这条链的一个节点,并定义好它们的顺序,当一条有发票申请的订单消息过来之后,会顺序的通过整条链的每个节点,当链中的某个节点可以匹配本次发票申请的条件时,就会被选出做发票申请单的提交,否则会交给下一个节点来做处理,这样以后扩展发票出口时,我们只需在链上增加节点即可,扩展性非常好。

相关内容已经整理到博客中,详见 责任链模式在交易系统中的应用


对数据的妥善处理

随着业务量的增长,系统的数据也在不断的增加,达到一定量级之后,就需要对单表进行水平拆分来保证数据库操作的性能,也就是我们常说的分库分表,拿我们的订单表来举例,通常这样的表我们会用订单id来作为水平拆分依据的key,这样可以保证数据的均匀分布,但拆分之后会带来另外一个问题,有些查询操作会跨库,例如我们查询某个用户的订单就需要查询所有的分库,无论你设计的跨库查询算法有多么牛,都无法避免跨库查询需要与多个库进行交互的天然劣势,所以这个时候我们一般会以用户id作为分区的key异构出用户维度的库,但我们如何保证多个维度的库的数据一致性呢,有人可能想到我们可以双写或者多写各个维度的库,这个方案有几个缺点,首先,增加一个维度之后对原流程入侵很大,我们需要在原流程上进行修改增加对新增维度库的写操作,导致原流程性能降低并且容易出错,其次,如果你的系统做的不够原子不够内聚,对订单的写操作可能分布在各个系统各个模块中,需要梳理出对订单的所有写操作,也很容易遗漏,一旦出现遗漏就会造成数据的不一致,针对以上问题我们决定在数据库底层来完成数据异构,如下图:

基于MySQL主从复制原理,对binlog进行增量订阅,这里我们采用京东自研的中间件BinLake,它会伪装成一个从库,与目标库完成主从同步这个过程,拉取到增量的binlog之后会加工处理并通过MQ发送给我们的应用程序,我们会在应用程序中将消息写入到其他维度的库中,这样可以保证数据一致性的同时还不会对原来的流程有任何的入侵。


对系统“无微不至”的监控

我们依托于京东内部的ump监控系统来完成对系统性能、可用率、健康状况等等多维度的监控,并采用AOP原理将监控切入到我们系统每个方法中,保证任何环节出问题我们都能及时的收到报警,并且会自动保留案发现场完整的线索,这样“无微不至”的监控可以让我们及时的发现系统任何的问题从而做出及时妥善的处理。


结语

整个京麦交易平台发展演进过程中遇到很多难点和痛点,它的生存环境很复杂,我们在解决难点和痛点的同时要保证系统稳定性,并且要对业务的频繁变动和扩展做很好的支持,这里用“化繁为简”这个词来为本篇文章做一个总结并画上一个句号。


本文受原创保护,未经作者授权,禁止转载。 linkedkeeper.com (文/张强)  ©著作权归作者所有