【资损】分布式环境中的幂等性控制设计
📫 作者简介:小明Java问道之路,专注于研究 Java/ Liunx 内核/ C++及汇编/计算机底层原理/源码,就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。
📫 热衷分享,喜欢原创~ 关注我会给你带来一些不一样的认知和成长。
🏆 InfoQ 签约作者、CSDN 专家博主/后端领域优质创作者/内容合伙人、阿里云专家/签约博主、51CTO 专家 🏆
🔥 如果此文还不错的话,还请👍关注 、点赞 、收藏三连支持👍一下博主~
本文导读
系统功能类设计在分布式的环境中,一个系统的设计除掉可用性问题外,导致资损的问题可以归结为如下三类: 一幂等控制问题,二兼容性问题,三并发互斥问题。本文主要介绍幂等控制问题。
一、 资损防控系统设计资损防控规范
从系统架构层面整体来看,支付公司的系统可以抽象为如下结构:
一、对外部商户提供收单服务类的系统
二、连通支付公司与各金融渠道的网关类系统
三、支付公司的内部业务处理系统
四、消息、调度等中间件系统
五、数据库、缓存等存储平台
从系统架构与业务架构上来讲,各个结构连接的地方最容易出现资损。因此我们将从接口服务层面与系统设计层面对资损进行分析并总结相关规范。
二、幂等性控制设计
幂等性问题是分布式系统设计中最常见的问题,是分布式系统服务的一种承诺,承诺只要调用接口成功,外部无论何种原因的多次调用对系统的影响是一致的。
在支付公司,我们常说的幂等性控制往往不是重复访问的只读结果一样,更多的是指业务活动、资源变更等重复请求,同一个服务操作实例最多只允许执行一次。
幂等性控制包括两个层级:
1、幂等性控制需要完成唯一性控制
主要根据幂等性(唯一性)控制字段即可完成。即:根据约定的幂等控制字段(唯一性字段),对业务重复提交、请求方重复请求、定时任务、网络原因等导致的重发的数据来判断是否重复提交。
如收单系统根据商户号+日期+外部订单号,拒绝同一个商户在同一天内重复发送同一个外部订单号的交易请求。
2、重复请求多次都不会对业务处理产生影响
在唯一性控制的基础上,如果要做更复杂的幂等性控制;这时候,需要和其他业务要素一起来进行判断。如果全部关键业务数据请求和现有的数据一致,可以使得请求一次和重复请求多次都不会对业务处理产生影响。
如在收单系统中,如果外部商户请求的“商户号+日期+外部订单号”与现有交易中的数据一致,并且对其他关键字段金额、收付款方等做比对;如果比对一致可以返回前面处理的结果,不一致可以提示商户“请求参数与原先参数不一致,请检查”。幂等性未做控制或控制不合理是系统设计中最容易产生资损风险的环节。
在系统设计中,各资金处理业务至少需要保证幂等性的第一层级“唯一性控制”。
三、常见幂等控制问题资损风险
1、上下游调用中,幂等字段使用自身产生的流水号做幂等控制容易出现幂等被击穿。
2、幂等字段的选用不合理。选错幂等字段,在业务上导致应该做幂等控制的未做或不该控制的被控制。比如本该选用交易流水号做幂等控制的选用了业务流水号。
3、幂等字段设计的全局性考虑不全。
1、空间全局性考虑不全。如外部商户请求的幂等控制,是使用“商户号+请求流水”还是使用“商户号+日期+请求流水”还是使用“商户号+日期+交易类型+请求流水”来设计幂等字段做幂等约束。2、时间全局性考虑不全。幂等字段的设计是幂等一天,还是三个月,还是永远。在我们使用数据库来做幂等控制,由于分区、归档等原因,会导致幂等失效。比如分区后建立的本地索引(非全局)的唯一索引,只会在当前区有效;数据归档之后,幂等控制无法判断归档走后的数据。
4、幂等底层控制设计不合理导致幂等失效。如使用 select 而非 insert 进行唯一性控制,高并发的时候就会使幂等失效。
5、消息、定时任务等非同步场景的幂等性未考虑。
6、上下游系统交互的幂等约定不清楚。如很多银行机构接口无幂等性保证,如果我方不清楚将框架设置为异常重发就会造成资损。如我们只约定了唯一控制的幂等约定,对哪些字段进行幂等第二层级的判断没有约定。
四、幂等性控制设计规范
1、幂等性字段规范
1、使用上游流水做幂等控制:上下游系统调用中,下游系统在可以使用上游系统的请求流水进行幂等控制时,不要自己生成流水进行幂等控制。
2、关键的资金业务处理,需要选用全局的唯一处理流水号做幂等控制。如账务系统、各渠道网关系统均需要选用 UTPP 的支付流水号来贯穿始终进行幂等控制,以保证幂等在整个支付过程中、在整个资金体系中全局有效一致。
3、选用幂等字段时候,除了理解其字段控制含义,还需要理解其业务含义;以达到选用合理幂等字段的目的。如上游有业务流水号、交易流水号,下游系统到底选用哪一个做控制,需要考虑其在特定场景下的业务含义。
4、幂等字段的选用要考虑其空间和时间的全局性、扩展性。要考虑选用哪些字段进行幂等控制、幂等字段流水号的长度够不够用。幂等控制时间是多少,由于数据库的归档、分区、分库等操作,会不会导致幂等失效;部分的数据库结构操作、数据操作甚至需要我们变更幂等的控制机制。
5、除了约定哪些字段进行幂等唯一性控制;对于实现了幂等控制第二层级的服务,还需要约定哪些字段用于幂等控制。如收单系统和商户约定了“商户号+请求流水”做幂等唯一性控制;还约定了“收付款方账号+金额”做第二层级的幂等控制字段,重复请求下“收付款方账号+金额”一致会返回交易状态,不一致会返回“请求参数与原先参数不一致”
2、消息处理、定时任务等非同步场景需要和同步场景一样考虑幂等性
1、对于消息的处理,不能依赖消息次序来进行业务处理;要考虑乱序、消息同步处理等问题。可以通过消息创建时间、现在的业务状态顺序判断消息是否过期;消费掉消息后是否需要再次进行业务处理。
2、定时任务执行的时间、次数都是不可控的。更需要通过业务状态、业务规则对定时任务进行幂等控制。
3、需要明确:消息处理、定时任务只是业务逻辑处理的一个触发,并且其触发是不保证次序和次数的,实际生产中消息中心消息重发、调度中心多次调度都是存在的;一定要保证在系统的业务逻辑处理中考虑业务的幂等控制。
3、幂等服务的约定
3.1 幂等性的处理机制约定
仅幂等唯一性控制,还是需要进一步根据哪些字段做幂等第二层处理。
3.2 幂等性交互的约定
上下游交互,需要明确幂等性处理的相关规约(如幂等异常处理机制、返回码、错误码等)。
3.3 服务重发的约定
1、交易类的服务,禁止网络自动重发。无论内部的 dubbo、外部的 httpclient、xfire 等禁止使用框架组件自动重发。
2、上游系统调用下游系统,请求未发送前,上游异常,可以置失败、进行重发。
3、上游系统调用下游系统,请求已发送再出现系统异常,必须向服务方确认状态后,明确可以重发才能进行重发。
4、不能明确可以重发的,就不要重发;遵循重复就严原则。
4、服务的幂等实现
常见的幂等实现方法,有如下几种:
4.1 幂等控制表实现
一般情况下,我们会在数据库上设计一张幂等控制表,并根据幂等控制字段建立唯一索引。在服务使用方请求过来的时候,先往幂等控制表 insert 一条数据,通过数据库本身唯一约束进行控制;当有重复请求,将不能 insert 重复的数据,有相关异常抛出,提示幂等控制表已经有同样数据存在。达到幂等控制目的。由于是基于数据库的幂等控制表的方案,当有数据库变更,尤其是幂等控制表有归档等数据迁移、分区、分表、分库等情况时候,将会导致数据库唯一约束失效的场景,需要着重考虑。
4.2 其他实现
也可以借助分布式缓存等进行幂等控制实现。此种方式不是建议使用控制方式,如果需要使用,请和各种域架构师进行评审。
总结
系统功能类设计在分布式的环境中,一个系统的设计除掉可用性问题外,导致资损的问题可以归结为如下三类: 一幂等控制问题,二兼容性问题,三并发互斥问题。本文主要介绍幂等控制问题。
版权声明: 本文为 InfoQ 作者【小明Java问道之路】的原创文章。
原文链接:【http://xie.infoq.cn/article/da187304a0e0dd7b2e06082e7】。文章转载请联系作者。
评论