陈旭琳
产品开发处
随着在线供应链金融平台客户规模不断增长,产品的不断增多,业务和管理也趋于复杂化,正朝着纵向供应链半径扩大、横向业务场景多元化以及外部协同能力多角度延伸,这都给系统的数字化开放和扩展能力提出更高要求,也随着我行分布式转型和面向服务架构转型,供应链系统也是紧跟步伐,以生态角色的视角来重构系统服务架构,在平时的设计实践中需要不断的思考怎样去避免“烟囱林立”的状态,打破边界,把具有共性的流程、属性和信息等抽象出来,变成一个公共的服务,使之在一定程度上脱离业务板块和系统的束缚,变成任何业务、随时随地都可以使用的服务,再实现对供应链整体业务价值链的反哺和赋能。本篇就异常交易补发的处理实践,探讨如何将异常交易技术部分和业务进行解耦。
01
我们的接口应该做成什么样子的
1、分析
实现微服务化的一个很明显的结果就是:被改造的系统中的很多独立的功能单元被设计成了一个独立的方法体而对外提供服务,开发人员称之为接口。其本质就是对外提供服务的一个独立单元,当请求方调用这个接口(这个接口我们后面都叫成服务)的时候,需确保将一个明确的返回结果返回给请求方。返回结果包含且只应该包含如下三种情况:
(1)业务处理成功
(2)业务处理失败(返回业务异常标识信息)
(3)系统级异常(try-catch捕获异常并返回统一的提示信息,而不是一堆堆栈信息)
我们绝大多数时候是通过数据库事务的统一提交和回滚机制来保证此服务中操作数据库多表的数据一致性。以此来保证不会出现一部分数据入库失败,一部分入库成功后的业务数据不一致的情况。但单靠数据库事务还不能解决所有问题。
下面探讨一下这样的场景:在微服务环境中经常有这样的调用:一个A接口对外提供服务,这个A服务又依赖于第三方系统平台的B服务(AB接口都涉及到更新DB操作),就会出现下面的隐患待解决:
服务A调用服务B,服务B被调用且业务处理成功,但自己的服务A自身更新数据库操作失败。这样就导致了全局业务上的数据不一致。
还有下面的场景:
还有这样的业务场景:移动端的手机支付服务。
对于这种业务场景如果我们使用简单的请求-应答的模式,当这个手机使用者进电梯或者其它网络闪断的情况下,很容易出现后端虽然处理成功,但手机展示失败的情形,然后使用者恐慌,重复提交支付,然后向客服投诉。
2、总结下来下面是我们需要攻破的点
(1)首先接口需支持幂等性,这是所有接口(服务)应该具备的。
(2)对于外部的请求,能够支持返回明确的结果。这话有些多余,但你见到的系统多时就会发现,很多的系统某些接口服务还并不支持这一起码的基本要求,或是因业务没设计好,或是代码不达标。
(3)支持异常补发,且补发机制不该过多混淆在业务逻辑代码中。
02
处理方案
熟悉hbase数据库的人都知道,HBase的WriteAheadLog(WAL)提供了一种高并发、持久化的日志保存与回放机制。每一个业务数据的写入操作(PUT/DELETE)执行前,都会记账在WAL中。
我们也采用这种记账的设计思路来实现类似于上面的支付接口服务A:即把此接口服务调用外部服务或流程中关键节点的结果记录到此WAL表中。然后利用定时任务来支持异常补发。
1、数据结构设计
设计一张记账表WAL,供平台所有需要支持异常补发的接口服务使用。
表结构如下:
ID物理主键
REQ_DATAS请求数据
REQ_TYPES请求业务类型
REQ_STATUS状态(1,初始状态2外接口处理成功3外接口处理中4业务异常5...)
UPDATE_TIME更新时间
将此表的状态字段贯穿在整个接口服务的执行过程中,处理成功后删除这条记录。
2、流程实现
3、定时任务设计
这个定时任务很纯粹,就是定时从WAL表中查询出状态为第三方支付接口处理失败的记录并重新调用,这样就完成了异常补发机制。当然,也可以为自身的业务异常做补发,相信大家能举一反三的。
4、代码实现
我们不能将类似于这种异常补发的非业务类代码过多的写入业务逻辑中,所以尽量抽出公共部分,在上面的设计流程里边,记账到WAL表中的业务处理对于每个需要异常补发的接口都相同。所以可以抽离出来作为公共部分。调用第三方服务和业务逻辑实现,是每个接口都不同的,由各自服务各自具体实现。下面是基于模板设计模式实现的关键代码。
模板模式模型:
(1)定义策略接口
(2)定义模板抽象类
(3)自己的业务service实现类
在上面的service实现类中,当请求调用execPayforOrder()方法时,方法体中执行其抽象类父类的方法。父类中写好了“记账到WAL表中的业务处理”的共通业务。调用第三方服务和业务逻辑每个接口各不相同,所有由各个业务service自己实现。当使用如上的设计和代码来实现异常补发,只需要在业务代码中加5行左右的代码即可实现异常补发。
至于前面的提到的移动端的手机支付服务场景,也可以套用此模式,在前后端架构设计的时候可以将后端代码设计成异步实现,因为接口支持了上面的幂等性和重放请求,所以就天然支持这种伪异步设计了。
03
总结
需要注意的是,上面异常补发的设计实现,是针对涉及到更新数据库(增删改)的接口而设计的,实现了上述实现机制,我们就能通过查看WAL表数据实时的监控到所有的接口是否有异常情况,在设计时通过对业务进行拆解,解构和重构,将明确的共性内容梳理出来,固化形成能力中心,技术部分通过和业务功能进行解耦,不但能够快速的感知接口的所有异常,并且通过实时调用补发或通过定时任务触发补发。
当然,条条大路通罗马,也许有更简洁高效的实现,欢迎大家指正并共同成长进步。
预览时标签不可点