可靠消息模式

近期项目的事一直很忙,发现要学习只能自己挤时间。最近大致看了下淘宝曾宪杰写的《大型网站系统与Java中间件实践》,感觉还是受益匪浅。这里结合自己的体会把可靠消息这块总结一下。

可靠消息,顾名思义就是在前置交易成功后系统能保证一定把消息发送出去,另一方面,如果前置交易失败,消息一定不能发送。可靠消息和事务交易还是有区别的,事务消息一般是通过事务管理器来保证消息系统和应用服务的数据库系统的一致性。由于消息系统和数据库系统一般是两个系统,所以事务消息一般采用两阶段提交来做,而两阶段提交在异常情况下问题很多,实际系统一般很少采用。可靠消息是事务消息的简化版,它提供了最终的一致性,同时有避免了厚重的事务管理器。

注: 本文介绍的可靠消息专指发送端一致性的保证,即保证消息一定能发送到消息中间件或接收端。对于接收端的消息一致性,即接收端一定能收到并处理消息不做阐述。接收端消息一致性的实现方式有很多,很多消息中间件的文档中都会提到,包括但不限于如下两种(1)接收端消息处理成功才认为消息接收成功,接收端进行幂等处理(进行判重或业务逻辑支持重复);(2)接收端采用拉取方式从中间件主动获取消息。

下面简单介绍一下可靠消息的实现方式:

1、本地事务方式

这种是最简单的方式,即在同一个本地事务中将待发送的消息放在单独的表中,并更新(或新增)前置交易的状态。详细处理过程如下:

联机交易处理过程:

  1. 启动事务 1.1 记录“待发送消息表”,状态为“待发送” 1.2 完成业务处理,更新(或新增)前置交易的状态,例如记录业务流水等
  2. 提交事务
  3. 进行异步消息发送功能。在异步发送消息确认成功后,将“待发送消息表”状态改为“发送成功”。

异常情况处理:

  • 启动自动任务定时扫描“待发送消息表”中的长期“待发送”的消息,然后进行消息发送。
  • 对于多次重试发送消息均失败的情况(可能消息系统故障,或应用程序问题导致消息格式不正确等),将“待 发送消息表”状态置为“发送失败”,后续由人工处理。

上述这种本地事务方式的优点是:

  1. 由本地事务保证发送消息和记录交易状态的一致性,而无需应用提供单独的查询交易确定交易的状态;
  2. 相比后面处理方案,无需考虑应用本身阻塞的问题,可保证消息永远不会漏发;
  3. 由自动任务保证消息最终一定会发送成功,从而保证了最终一致性。

缺点是:

  1. 需要保证消息待发送表和业务流水信息表在一个数据库中,在分布式环境下,相当于每个数据库上都需要有这样一套表;
  2. 需要应用本身支持事务,且需要应用明确记录待发送表,或通过框架隐式将业务更新和消息记录进行事务耦合;
  3. 需单独的自动任务对待发送消息表进行轮训、更新,增加了数据库的开销。

2、预发送及确认机制

这是曾宪杰书中提到的方式,即在应用记录前置交易状态之前、之后反别发送预发送消息、确认发送消息。详细处理过程如下:

联机交易处理: 1. 发送预发送消息:接收方一般为消息中间件或其他单独消息服务,此系统需保证持久化后再返回成功。消息中需包含应用交易标示信息,以便能在后面确认应用是否处理成功。 2. 完成业务处理,更新(或新增)前置交易的状态,例如记录业务流水等。 3. 发送确认消息:接收方收到消息后进行消息发送,并更新本地的消息状态为发送成功。此步骤可以异步来做,以减少联机交易等待时间。

异常情况处理:

  • 消息中间件或消息服务对于长时间处于待发送的消息,需要调应用提供的查询交易确认实际交易结果,如实际交易成功则发送消息,如失败则取消发送。
  • 需要注意应用查询交易返回“处理中”或“无此流水”的情况,这时由于应用本身可能存在阻塞,故不能盲目将消息置为失败。建议置入异常消息,进行人工处理,或设置很长的超时时间,超时后置为失败。
  • 对于消息服务实际发送消息时的失败,需要重试进行发送,对于长时间发送均失败的情况,需要置入异常队列,由人工处理。

上述处理方式的优点是:

  1. 只需要应用增加一个预发送的消息,即可保证消息可靠性
  2. 由单独的消息中间件来保证消息发送,避免了与应用过多耦合
  3. 可以严格保证消息的最终一致性,而无需引入事务的开销
  4. 可扩展性好,各组件功能切分明确,对应用侵入性小

缺点是:

  1. 当应用阻塞时无法断定交易即为失败,超时时间的设定有经验的因。为防止事务挂起,异常情况只能人工确认。系统无法做到消息发送和业务功能的强一致。
  2. 应用需额外开发查询交易,来确认交易的结果。此交易需要保证幂等性。

3、接收方定期校对方式

当发送方无法或不愿意进行改动以保证消息一致性时,可以通过接收方的定期校对来保证消息的最终一致性。大体实现逻辑如下:

联机交易处理:

  1. 完成业务处理,更新(或新增)前置交易的状态,例如记录账务流水等。
  2. 发送消息。(发送消息可能会丢失,发送方不进行任何保证)。接受方收到消息进行相应处理(异步)。

异常情况处理:

  • 当接收方没有收到消息时或收到消息但业务处理异常时,需要根据定时策略,定时调用发送方的查询交易进行查询并校对,对遗漏的消息进行补偿处理。

上述处理方式的优点:

  1. 对发送方无约束,其可以不保证发送消息一定发送成功;
  2. 可以适用于消息发送渠道不可靠或即使收到消息也无法保证消息被处理的情况。比如移动手机端的消息推送;
  3. 无需消息中间件支持。

缺点是:

  1. 定期校对功能的实现开销,比对哪些消息没处理当交易量大时并不是一个简单的工作。
  2. 需要发送方提供业务查询或下载的功能,以便确认漏处理的消息。
  3. 接收方需要进行消息幂等处理(其实不算缺点了,上面两个方案也需要进行此保证)

上面简单介绍了可靠消息的设计思路,大家可结合自身应用的特点进行选择。可靠消息本身是有额外开销的,我们能做的只能尽量减少对应用的影响。实际中消息中间件的实现还有其他考虑的内容,比如消息存储、性能、应用如何处理中间件故障等等,大家可以参考曾宪杰书中的内容。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《ITechLib》

账务系统热点帐号问题

账务系统的热点帐号是一个很常见的问题,特别是随着互联网支付的发展,热点帐号的问题更加突显。很多支付机构由于业务限制往往在一个银行只有一个对公帐号,这一个帐号往往就是热点帐号。

对于不同帐号的并发处理相对比较简单,比如按客户或帐号拆表、拆库,这之间比较复杂的是分布式事务问题。目前处理分布式事务一般的解决方案包括:

  1. 可靠消息+补偿模式;
  2. TCC分布式事务模式;
  3. 两阶段提交;
  4. 拆分为多个局部事务。

这些方案往往通过自动任务来处理异常的场景从而保证最终一致性。

如果说去IOE最困难的问题应该就数热点帐号问题了(或者说分布式事务问题),因为单个帐号的扣减余额等肯定要通过事务来保护,防止出现余额和流水不符的情况。但事务必然会导致并发度下降,即使对于功能很强大的主机核心系统,并发度也会显得捉襟见肘。为了防止自己处理能力超限,主机往往会进行流控以保护自己,但这样的用户体验会很差。如果这件事在开放系统中做,由于每个服务器性能有限,并发度会更低。下面介绍下自己对热点帐号问题的理解,因我本人并不负责核心系统,有不当之处欢迎指正。大体解决方案分为如下几种:

1. 汇总记账方式

汇总记账分为两种,收单和付款。其中收单采用异步入账的方式比较好,可以将钱先放在内部过渡户中(过渡户可以是一个或多个),然后异步将钱整批汇总后入收方帐号。对于付款会复杂些,主要涉及到透支的问题,如果业务上允许透支也可以采用先银行垫付一段时间,然后整批一笔入付方帐号;如果业务不允许透支就比较复杂了,一种可行的方案是将每笔账务先预先记录流水然后返回“处理中”,后台采用批量的形式以批量为单位更新付方帐号的余额。但这样带来的一个问题是响应不够及时。

2. 异步缓冲记账

参考资料1中提到的削峰填谷方式,思路比较简单,对于不超过并发数的交易同步响应,对于超过并发的交易放在缓存队列慢慢处理,可以返回处理中或成功(返回成功有透支风险)。这种方式本质并没有提高系统的并发量,只能算是一种简单体验改进。

3. 同步批量缓冲记账

在参考资料2中提到了一种可行的解决方式,但并没有介绍如何实现,结合参考资料3,可行的解决方案如下。当接到客户的转账请求后,如果为热点帐号,则在执行完所有的校验后,将交易放在一个队列(或多个队列)中。针对每个队列会有单一的线程进行处理,它一次或获取多条待处理记录然后统一记录数据库流水,对于热点帐号的余额扣减只扣减一次。对于这种方法如何确定队列的大小以及同步等待的时间是一个需要分析的问题。这种方法可以成倍减少数据库的压力,类似与数据库事务管理器的提交机制,但它也带来一个问题就是增加了单笔交易的延时。为了使这种方法收益最大化,在框架设计层面需要把同一个热点帐号的交易转到特定的服务器来处理。

4. 建立多个影子帐号

上述“同步批量缓冲入账”方式的限制是单台应用服务器的处理上限,单台服务器的处理上限。为了解决此问题,一种可行的解决方案是将一个热点帐号拆分为多个影子帐号,每个影子帐号有自己的余额和流水信息。所有影子帐号的余额之和是整个帐号的余额。拆分后的处理逻辑和单个帐号的处理方式相同,可以采用不同的处理方式,如异步缓冲记账或是同步批量缓冲记账等。这个方案需要特殊考虑的点包括:

  1. 帐号余额增加时如何分配给其他的影子帐号:为避免分布式事务,建议采用异步方式处理。当然由于这个交易量很少,采用分布式事务也是可行的。
  2. 当单个影子帐号余额不足但整体帐号余额充足时的处理方式:可以采用“直接报错“”或是“返回处理中”改为异步处理。对于客户的特殊提取交易不应直接报错,因为报错则无法支持客户转出所有余额。对于异步处理的方案在返回处理中时可将此笔账务交易转移到其他影子帐号处理,如果所有影子帐号均余额不足但整体余额充足时,此时需要进行影子帐号间调账,将此笔交易放入特殊队列,将此影子帐号置为停用,在处理时将此影子帐号的钱转给其他影子帐号然后继续处理。直到所有的帐号均不支持则进行报错。可以看出当开始停用影子帐号时系统整体的并发度会显著降低,所以采用影子账号应让客户知晓,并提前做好客户的工作,例如帐号至少要保留特定余额、指定特殊的销户流程、与客户约定好各种异常的处理策略等等。
  3. 多个影子帐号之间需要保证负载均衡问题:另外需支持动态增加和减少影子帐号的个数。

另外提一下,内存数据库并不是一个好的解决方案,首先如何保证内存数据库和关系数据库的数据一致性是一个困难的问题;另一方面内存数据库本身也是一个单点,会存在单点问题;再一方面关系数据库本身对于热点数据也会做内存级别缓存,所以内存数据库意义并不是很大。

上面介绍了很多方案,可以看出并没有完美的方案,热点帐号方案更多的考虑是一种权衡,我们需要根据不同的情况来指定不同的策略。后续我会试着对于方案三、四进行实现,看是否有其他问题。后面如发现更好的解决方案也会更新此文章,也欢迎大家告知其他解决方案。

参考资料:

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《ITechLib》

应用系统无事务时如何保证交易一致性

在进行应用系统开发时一笔交易往往需要多次操作数据库,为了保证交易的一致性、正确性,简单的方式是通过数据库事务来控制,多次数据库操作要么均成功要么均失败。但有时我们不能将多次数据库操作封装在一个事务中,主要有两方面原因:1.多次数据库操作中间需要调用外部服务,而这个外部服务比较耗时,此时如果统一封装在一个事务中会长时间占据数据库连接,可能导致长时间锁定某些资源,从而严重影响系统并发度;2.随着异构系统越来越流行,一个系统可能同时有多个数据库,或是同时有关系数据库、Nosql数据库、分布式缓存、消息队列等,如何保证这些系统之间的一致性,可以使用“两阶段提交”或PAXOS等分布式协议,这样的话会显著增加系统的延时,降低系统的吞吐量,另外当有异常时处理也很复杂,会有超时、死锁等问题。

其实对于应用系统来说,如果不是要求“实时一致性”,只是要求“最终一致性”,是可以通过系统设计做到不使用两阶段协议也可以保证一致性的(参考Idempotency Patterns)。下面先表述几个概念:

  • 一致性:其实就是同一系统或多个系统对于某个数据状态的一致意见,这也包括从外部调用者来看状态是一致的。在数据库系统中只要记录了提交日志,事务就相当于成功了。这里日志就相当于合同契约,所以日志必须持久化,否则就没有意义了。在分布式系统协议PAXOS中其实多个系统最终也是得到了一份契约,所以PAXOS也可以作为类似两阶段协议这样使用。
  • 最终一致性:系统在运行中或发生故障时处于某个“临时状态”,经过一段时间后会重新进入到一个“一致状态”。注意这里的“临时状态”并不是错误状态,只是说它是一个中间状态,这个中间状态也可以是正确的,只是不是最终状态而已。如果能做到这个中间状态对于客户是透明的就更完美了(数据库系统就做到了)。这里不提什么“弱一致性”,弱一致性其实就是不保证一致性,这只是业务对于系统设计问题的容忍,如果你是做账务系统,就不要考虑弱一致性了。

其实很多系统仅要求“最终一致性”即可,如果系统可以做到正常情况下完全一致,异常的情况下一段时间(如5分钟内)做到交易结果一致,客户应该都没什么问题。时间也是我们的朋友,虽然这个朋友在多个机器上不完全一致,但是误差都很小,在系统设计时可以加以利用。

下面主要介绍在不使用事务的情况下如何保证系统正确性,对于如何使用“两阶段提交”(如JAVA XA规范的分布式事务)可以参考其他文章(推荐这篇)。我们这里主要依靠下面几个工具:

  1. 状态:当前交易所处的状态,状态要尽量少、明确,要以事务边界为粒度。状态应避免循环,尽量采用顺序前进的方式。对于需要重复或是循环时可以设置子状态,以保证业务逻辑清晰。
  2. 自动任务:对于中间状态的记录进行处理,使其恢复正常状态。此任务应该考虑时间,防止过早调起,将正常交易作为中间交易进行处理。
  3. 时间:要记录数据的最后更新时间,通过时间确定记录是真的异常还是正常处理中。不同的系统的时间间隔应该不同,具体根据业务需要和技术实现来定。
  4. 可测试性/幂等性(重发):可以参考我的另一个文章《系统重发设计》。

系统内部设计

下面先介绍单个系统内的处理,假设一个系统涉及如下处理逻辑:

  1. 系统收到交易请求,进行交易输入合法性检查,如检查失败直接报错。
  2. 业务合法性检查,如合法性检查失败则记录交易失败信息。
  3. 调用外系统交易进行信息登记。(可能会有异常)
  4. 系统内部进行限额扣减。
  5. 调用外部系统进行转账处理。
  6. 记录转账结果
  7. 如转账失败则回冲限额(这里为简单起见,假设前面的信息登记动作由其他自动任务保证一致性,否则此处需增加外呼冲正)
  8. 返回交易结果。

针对上面的场景应该如何设计系统呢,最简单的设计是把所有这些全部放在一个事务内,由事务保证一致性,这样的话事务很长,如果遇到外呼超时会严重影响系统性能。如果采用状态来控制应该采用下面步骤:

  1. 定义状态,根据前面的状态规范,尽量少、明确,以外部系统为边界。可以看出可以分为如下状态:1-待登记;2-处理中(登记完成,限额扣减完成,待转账);3-交易成功;4-交易失败。
  2. 确定合适的记录数据库时机,延迟保存原则,只在状态需要变更时保存流水,且预计流水(参考数据库的先记日志原则)。系统内尽量通过事务保证一致性。
  3. 定义自动任务,对各种中间状态(此处为1-待登记;2-处理中)进行处理,通过重发或是查询外系统的结果来确定后续做法。需要和对方系统约定处理方式。

下面展示下设计后的系统处理模式:

  1. 系统收到交易请求,进行交易输入合法性检查,如检查失败直接报错。(无DB操作)
  2. 业务合法性检查,如合法性检查失败则记录交易失败信息。(若失败记录4-交易失败)
  3. 调用外系统交易进行信息登记。(可能会有异常)(外呼前记录1-待登记) (外呼后若失败,则记录4-交易失败)
  4. 系统内部进行限额扣减。(扣减限额与更新流水放在一个事务中,状态改为2-处理中)
  5. 调用外部系统进行转账处理。 (外呼后若失败,则记录4-交易失败)
  6. 记录转账结果(若5成功则更新状态为3-交易成功)
  7. 如转账失败则回冲限额 (若5失败,则记录4-交易失败,同时回冲限额,放在一个事务中)
  8. 返回交易结果。

对于上面的状态如果想保存中间结果,例如登记成功的结果、外呼失败的信息,可以通过子状态来做,或是增加状态。但这样会增加自动任务处理的复杂度,需要做更多的判断,好处是可以一定程度减少外呼查询或其他动作。如果系统内部的多数据库操作不想使用事务封装或是无法封装(可能涉及多个模块),也可以通过状态区分开各个阶段。

系统间设计规范

多个系统内一致性控制需要采用分段控制。即每个系统都要保证自身的一致性、正确性。调用方系统保证消息被正确传递,并保证最终更新交易结果;被调用方保证自身的一致性,实现自身状态可测试性或是幂等性。系统之间的消息,采用唯一标识区分,被调用系统提供查询消息结果接口,或支持幂等性(可重发)。此唯一标识由调用方生成,调用方系统在调用外呼系统前记录此唯一标识,被调用方通过此唯一标识实现交易幂等性。如果被调用方支持多个调用方,可以通过调用方编号+唯一标识来作为唯一标识,或是指定统一的标识生成规则,由各方遵守。

如果存在多个系统串联的情况,可以通过这种方式来实现正确性传递。注意这种传递只能是从前->后,而不能由后面的步骤保证正确性,因为后面方法可能会没收到消息。

本文所述方法可以从系统设计层面保证交易的正确性,可简化记为:状态 + 自动任务 + 时间 + 可测性/幂等性 = 最终一致性。当然了这种方法也有弊端,1.应用设计需要考虑各种状态转移,系统在记录状态同时,需要记录时间、唯一标识等额外信息,增加系统复杂度。2.自动任务需要详细设计,通过时间保证和联机交易不同时执行,另外需通过乐观锁保证状态仅被自动任务更新(建议update时进行原状态对比)。3.后一步结果严格依赖前面的执行结果,只能采用串行模式。4.需保证中间状态对于客户是无害的,包括查询结果和实际业务效果(这一点有时很难做到)。

本文为我个人的一点经验总结,希望能对大家设计系统有帮助,大家有更好方式也欢迎指出。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《ITechLib》

账务交易一致性设计概述

对于账务交易最重要的就是交易一致性,即给客户反馈正确的一致的结果。交易一致性并不是一个简单的话题,说来话长了。下面我结合项目的一些经验,谈一下我认为重要的地方。

交易本身大体分为三类,联机交易、批量交易、批量联机交易(即联机实时返回多笔交易的结果)。下面分别对于不同的场景进行说明:

1 联机交易

1.1 接口设计

从接口设计角度,对于联机交易一般包括两个接口,账务发起接口、交易结果查询接口。其中联机交易接口用于发起实时交易,并实时返回结果。交易查询交易则用于对账使用,用于确认账务成功与否。

账务发起接口:

此接口输入至少应包括如下字段:客户标识、客户交易流水号(用于客户对账使用)、其他账务相关的要素包括付方账号、收方账号、金额等(具体根据业务需要来定)。

输出至少应包括如下字段:交易结果代码、错误码、错误信息、系统交易流水号(可选)、交易日期(可选)。交易结果代码包括成功、失败、不确定三种状态,其中当交易超时等异常时反馈不确定状态,由客户调用“交易结果查询”接口进行对账。另外两个可选字段系统交易流水号用于表示本系统内部的交易流水号,方便排查问题;交易日期表示交易发生的记账日期,以便客户知道这笔账属于哪日的交易,如失败“交易日期”可以为空。接口务必保证交易结果代码的正确性。

交易结果查询接口:

此接口仅用于账务交易反馈不确定或超时时对账使用,对于需要列表查询的需求建议另外使用单独接口来做(例如支持分页,支持模糊查询等),这主要是出于性能、可维护性如流控等方便的考虑。对于交易结果查询接口输入字段应包括:客户交易流水号、系统交易流水号(两者二选一输入)、客户标识,输出字段除了包括账务发起接口的交易结果输出字段外,还应该包括查询结果代码、查询错误码、查询错误信息,用于反馈这笔查询交易是否查询成功,是否应该后续继续查询。对于同时包括报文头、报文体的交易,查询结果代码等可以放在报文头中,而交易本身结果可以放在报文体中。

1.2 其他说明

上面提到的客户交易流水号由客户生成并上传,生成规则应保证同一个客户内部全局唯一,以便用于对账。注意不要采用一天内唯一的概念,由于时间并不是一个双方都认可的值,特别是涉及到日切时。可以采用一周内、一个月内唯一的概念,这需要在接口中提前约定。此客户交易流水号涉及到判重的问题,系统应保证交易唯一性,建议通过数据库唯一索引来做(具体实施方式将在后续文章介绍)。

系统交易流水号由我方系统生成,应保持全局唯一性,此流水号建议包含日期、时间信息,以便排查问题。

交易结果代码:由于此代码十分重要,程序务必保证其正确性。应用应将此字段放在应用能够完全控制的地方,例如对于报文包括报文头、报文体两部分,而报文头由框架负责填充,此时建议将此字段连同错误码、错误信息一起放在报文体中,对于报文头中的错误和客户约定为”处理中”状态,这是为了避免由于框架原因而导致原本正确的交易返回失败。此规则适用于账务发起接口及交易结果查询接口。

如需提供交易结果列表查询功能,应发布单独接口。此接口应支持分页查询、或是查询下一页功能(可通过在输入中增加上次查询最大系统流水号字段实现),支持按账号、按金额、按日期等进行筛选。注意此处日期包括:客户提交日期、系统营业日期、记账日期,这些日期概念是不同的,由于此接口仅用于查询不用于对账,如果业务允许可以仅支持一种,建议使用客户提交日期。

1.3 交易超时设置

交易在每个外呼中均应该设置超时时间,超时时间应该满足“漏斗原则”,防止在后端未处理完成时前端已经失败返回。漏斗原则是指从客户前端->中间转发服务器->应用服务器->账务核心服务器,各个节点的超时时间应该以此减少。对于特殊情况不能满足漏斗原则的情况,应该由应用服务器在程序逻辑上保证交易一致性,例如返回处理中并提供异步对账功能,或是提供冲正功能。若采用冲正方式,违反漏斗原则会造成很多交易结果不确定,交易应该在设计阶段考虑各种情况下超时如何保证交易一致性(冲正比较复杂,在此不做详细介绍)。

交易超时还包括一点:避免异常情况下查询交易早于账务发起交易到达。这一方面可以通过控制查询交易和账务发起交易的间隔来保证,例如约定超时时的查询间隔30s,但即使这样在特殊场景也会有问题。为了防止此问题可以在交易报文中设置交易发起时间、交易剩余处理时间,这两个字段在进入我系统后由前端系统生成并赋值(不能由客户填充),如客户通过外围系统接入此字段由外围系统填充。交易剩余时间一般设置为30s,在消息传递的各个环节应该对剩余时间进行重写(包括中间系统、应用系统等),如剩余时间为0,则不应该进一步传递消息,应直接返回失败。对于应用系统(即需要保证交易正确性的系统)在记录流水前也应该判断此剩余时间,如为0则应直接返回失败而不记录流水。

应用设计者应了解每只账务交易的一致性保证机制,从设计上保证一致性。对于账务交易尽量不要支持重发,重发很容易导致交易重复提交,且不容易识别查询返回结果,除非服务方和调用方系统进行周密的设计不建议采用。

2 批量交易

2.1 接口设计

批量交易是指客户通过上传批量文件的方式发起的账务交易,这种交易一般为异步处理。一般包括批量文件上传交易、批量结果查询交易。

批量文件上传交易

批量文件上传交易主要完成客户文件上传功能,系统如接收文件成功应返回客户文件接收成功。接口输入报文应包括客户标识、客户批次流水号(用于查询批量整批结果)、批量文件路径及名称、批量总笔数、批量总金额。输出字段和联机单笔交易类似,至少应包括如下字段:批量交易结果代码、错误码、错误信息、系统批次号(可选)、交易日期(可选)。

接口的其他注意事项包括交易结果代码、唯一性约束和联机接口一致。除此之外还有以下几点需要注意:

  1. 客户应保证批量文件早于联机报文到达,以便根据报文解析文件时文件已经发送完毕;
  2. 对于批量文件名称应保证唯一性,可以对于每个客户使用单独的目录。这一点需要和客户提前约定;
  3. 文件应使用特定分隔符分隔,需要保证分隔符和正常业务字段不重复,例如 @ # 等;
  4. 文件内每笔交易应包含客户交易流水号或批内子序号,用来查询结果时标识每笔明细;
  5. 系统对于批量的联机处理,至少应包括记录批量流水记录;对于后续处理如文件解析、校验,记录批量明细信息、业务逻辑校验,业务处理可以根据业务、性能需要考虑是否包括在联机处理中。
  6. 系统联机处理时应对每批文件的笔数进行控制,具体可以根据业务需要来,可以为200笔或2000笔。

批量结果查询交易

客户通过此交易可以查询批量处理的结果,包括两部分:一是批量整批的结果,一是每笔批量明细的结果。为了处理简单,可以当批量整批处理完成后再返回批量明细的结果,如果确实需要实时返回批量明细的结果,需要在设计时保证返回结果的正确性,包括明细的完整性、正确性(如批量太大不建议采用这种方式)。

输入接口应包括: 客户标识、客户批次流水号、系统批次号(可选);

输出接口应包括:1.整批信息:批量交易结果代码、错误码、错误信息、系统批量流水号(可选)、交易日期(可选);2.明细信息:客户交易流水号/批内子序号、明细交易结果代码、错误码、错误信息、系统批量子序号(可选)、交易日期(可选)。明细信息可以根据接口定义是否返回,如只有整批处理完再返回结果,可以定义为:当整批信息不为整批失败、处理中时会返回交易明细结果,当整批结果为交易成功时应返回明细结果。

这里需注意以下几点:

  1. 上述输出接口字段建议放在报文体中,通过报文头的系统交易结果、错误码、错误信息来表示查询交易本身的结果,如交易超时等;
  2. 如系统支持的批量文件较大时,需要通过文件来返回交易结果,返回报文中没有明细信息,但应该有明细结果文件路径及名称。文件名可以和客户输入文件名加后缀方式来对应。结果文件建议作为批处理的最后一步给客户提前生成好(建议在修改整批状态之前,以免客户查不到文件),并和客户约定保存多长时间。
  3. 为防止批量状态卡死,系统应该有专门的检测自动任务来检查异常的批量状态,并针对错误进行相应的处理(包括转人工处理)。
  4. 批量结果查询交易应和批量历史流水列表查询分开,以保证系统性能和可维护性。

2.2 其他说明

批量交易的其他注意事项,如判重、交易结果、超时,可参考联机单笔交易中的说明。在设计批量交易时应将整批作为一个整体考虑,结合系统非功能要求确定应支持的功能点,切记为了所谓业务需求,盲目实现不必要的功能。

3 批量联机交易

这类交易相当于系统提供的单笔交易的批量接口,但整批的结果要求同步返回(如不同步返回可以参考上面的批量交易)。这类交易比较特殊,为了保证交易一致性,需要将这多笔交易当做一笔来看待,作为整体记录流水,而不是简单的循环调多笔。批量联机交易一般不要直接发布给直连客户,可以作为界面或是内部调用的性能优化手段,如果没有性能问题应尽量采用循环单笔的方式。

输入接口应包含:客户标识、客户交易流水号[支持列表]、其他单笔交易的业务字段[支持列表]。输出接口应包含每笔交易的结果信息列表。对账接口应支持按照客户交易流水号列表来查询结果。

在交易设计时需要考虑如下几点:

  1. 对交易支持的最大笔数进行限制,这个限制的建议值为超时时间内能处理的交易笔数(包括启动多个并发、不启动并发两种情况),最大值为系统可以保证交易一致性的最大笔数。
  2. 交易处理方式可以考虑以下几种:a.将多笔交易作为一个整体提前记录流水信息,然后逐笔处理[建议];b.直接调用逐笔交易当做单笔处理,如已超时未处理记录直接返回失败,不做处理[可以满足一致性要求,但不建议];c.直接当做单笔处理,超时仅返回处理中,由判重功能来保证不会有账务问题。[不建议,客户无法知道应该等多久查结果、再次发送]
  3. 查询结果交易需要保证在交易处理的任何时点的结果都是正确的,不能仅查到部分交易的结果,故应该将多笔当做一个整体来预计流水。
  4. 多笔交易一般是有关联的,建议对中间数据进行交易级别缓存,以提高性能。
  5. 是否要采用并发处理建议能支持客户选择,由于并行处理对于某些场景是会影响交易正确性的。(PS.有些客户是在乎顺序的)
  6. 此交易会长时间占据联机服务器的线程,会大大降低系统的吞吐量,采用并发并不会使情况缓解。针对此交易设置单独限流策略。

上面对于三类交易的接口设计以及一些注意事项进行了介绍,后面文章再对账务交易的一些设计细节进行详细描述。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《ITechLib》