资损分布式系统并发互斥设计
Posted 小明java问道之路
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了资损分布式系统并发互斥设计相关的知识,希望对你有一定的参考价值。
📫作者简介:小明java问道之路,专注于研究 Java/ Liunx内核/ C++及汇编/计算机底层原理/源码,就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。
📫 热衷分享,喜欢原创~ 关注我会给你带来一些不一样的认知和成长。
🏆 InfoQ签约作者、CSDN专家博主/后端领域优质创作者/内容合伙人、阿里云专家/签约博主、51CTO专家 🏆
🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~
本文目录
本文导读
并发互斥控制设计在整个分布式系统中,大到业务的并发处理、服务的并发请求,小到数据库表的并发读写、java对象的多线程访问,并发无处不在。因为并发,所以互斥。
一、 资损防控系统设计资损防控规范
从系统架构层面整体来看,支付公司的系统可以抽象为如下结构:
一、对外部商户提供收单服务类的系统
二、连通支付公司与各金融渠道的网关类系统
三、支付公司的内部业务处理系统
四、消息、调度等中间件系统
五、数据库、缓存等存储平台
从系统架构与业务架构上来讲,各个结构连接的地方最容易出现资损。因此我们将从接口服务层面与系统设计层面对资损进行分析并总结相关规范。
二、系统层面的并发互斥控制
我们所说的系统层面的并发互斥控制,主要包括:
1、服务的并发请求控制(进程级)
服务的并发请求控制,服务的并发请求既包括dubbohttp等同步调用的场景也包括消息、调单等异步调用场景需要一起考虑。
此外,在业务处理中,还存在各种互斥的资金处理的场景与流程,也是我们需要在设计的时候考虑的。
2、服务内处理的并发请求控制(线程级)
服务内处理的并发请求控制,内存中的并发控制。主要是内存中的对象、线程变量等资源共享资源进行并发控制。
内存外的并发控制。主要是指内存外的存储资源,如数据库表的访问等的并发控制。
三、常见并发互斥类资损风险
1、无状态迁移变化的控制
在互斥的业务流程与场景中,业务处理过程中,无状态迁移变化的控制。复杂的场景中未使用状态机。或者状态机状态迁移合法性验证不全面。或者状态机设计不合理,使得状态迁移变化不可靠。
常见的状态机设计问题有:
1状态被执行有多次。如支付成功多次可能导致业务被推进多次。
2、终态还可以继续变化。如代发成功又变更为待支付。
3、互斥状态没有做并发控制。如发货和退款同时发生,同时成功。
4、状态机没有统一控制。
5、多个状态机耦合在一起,状态控制混乱。如交易核心支付状态、交易状态耦合在一起,由于不同的状态控制,导致支付状态成功交易状态失败的问题。
6、状态机的状态迁移被漏处理。
2、并发请求处理中,没有考虑到消息与调度场景
并发请求处理中,没有考虑到消息与调度场景,比如收单服务请求支付核心进行支付处理,同步响应结果和消息通知结果同时到达,未做并发处理,业务被推进了两次(可能申购了两次基金、也可能通知商户发货了两次)。
3、对内存中资源未做并发控制或并发控制不严格
多线程环境下,对内存中资源未做并发控制或并发控制不严格,尤其是对线程变量的使用。
如对共享数据访问不加锁、单例对象进行写操作、线程变量进出不清空等均会导致并发控制问题。
4、数据脏读问题
数据脏读,并根据脏数据做判断。需要注意的是,不仅仅是对数据库中的数据访问存在脏读,分布式缓存中的数据访问也存在脏读。
5、常见的数据库锁操作问题
为了防范脏读脏写、不可重复读等常见的数据库并发问题我们常用锁机制进行处理;不合理的锁使用会导致风险。
常见的问题有:
1、先读后锁而非先锁后读。读的数据不能保证是最新数据,会导致仍然基于脏数据做判断的风险。
2、批量update流水的时候,where条件中没有原有流水的状态,且没有判断更新条数。容易出现不可重复读的问题。
3、SQL语句中使用updatesetselect完成数据的更新,有可能导致update锁机制不生效。
4、锁没有顺序且未设置超时等退出机制(如SQL中未加nowait)导致死锁。
四、系统并发互斥设计
1、业务状态比较复杂的场景,使用状态机进行状态控制
业务状态比较复杂的场景,使用状态机进行状态控制,状态机是针对业务状态比较复杂的场景下,并发互斥控制比较常见的设计模式。如统一订单交易平台均使用状态机来进行业务状态的管理。
状态机设计过程中需遵循如下规范:
1、由于业务状态的迁移,可能由同步服务请求、异步消息、后台调度、用户操作、运营操作等多种情形触发。在此情况下,需要保证状态机统一进行控制,不能设计多个状态迁移变更模块。更不能设计多个状态机进行对不同情形做状态迁移。
2、要设计终态,且保证终态不能被继续迁移。如一笔交易订单的超时关闭就是终态,在设计上就要避免其不能再次迁移为待支付。
3、要保证状态迁移和业务处理的一致性。
4、设计中,针对状态迁移的处理,需要对所有可能的状态做判断。极端情况下可以判断所有的状态。
5、由于业务状态比较复杂,为防止状态机状态设计腐化,与状态机实现代码表现不一致。状态机不能只根据文字描述进行开发,要先设计或更新状态迁移表或状态迁移图;可以明确表达出入口状态(初始状态)、终态、状态跃迁路径。
6、不能用同一个状态字段表达多种状态迁移。简单的业务处理,可能不使用状态机进行状态迁移的并发控制。但仍然需要使用统一模块进行状态控制、并考虑多种情况下的并发请求做好业务的并发与互斥处理。
2、并发请求的处理
并发请求的处理,针对分布式环境下的并发请求的处理多是基于数据库或分布式缓存等资源等进行并发访问控制。
常见的并发访问控制模型主要有如下几种:
1、基于资源的并发控制(悲观)。
这是比较经典的并发控制模型,使用较多,如账务系统中的记账操作就使用该模型控制用户账户余额的并发更改。
2、基于资源的并发控制(乐观)
由于是在第4点更新领域对象的时候检查并发,其实整个过程中并没有锁定任何对象和记录。所以采用该机制的时候,系统应该要容许不可重复读问题的出现。
3、基于分布式锁服务的并发控制分布式锁可以借助分布式缓存实现。
3、数据操作的规范。一锁二判三改
1、一定要保证先锁后读,避免脏读。数据库可以使用selectforupdate这种模型进行加锁。
2、提交的时候要判断更新的数据的状态是否符合要求,避免不可重复读。要在SQL的where条件中增加原有流水的状态,且检查了update方法返回的记录条数是否满足预期。如果是状态机的状态进行update的时候,要基于状态迁移来写where条件中的前置状态值,wherestatus=xx或者wherestatusin(xxyy)。update的结果,影响行数0、1N的时候处理逻辑均需要实现。
3、不要使用updatesetselect完成数据的更新有可能导致update锁机制不生效。
4、多线程操作规范
多线程操作规范需要遵循常规的java并发操作规范,保证操作的原子性。代码开发的指导原则应该是:尽可能的避免在多线程间共享数据、共享操作;如果要共享数据,需要对共享数据加锁;尽可能的使用spring提供的单例框架配置单例的bean;不要对单例的bean进行写操作;业务代码开发过程中,尽可能的避免使用线程变量使用线程变量的时候,一定要在出入口进行清空。...。。
5、避免死锁
无论是数据库还是分布式锁服务或者jvm内部的锁,在可能发生死锁的场景,加锁的时候需要通过加锁排序和设置超时时间等方式避免死锁。
总结
并发互斥控制设计在整个分布式系统中,大到业务的并发处理、服务的并发请求,小到数据库表的并发读写、java对象的多线程访问,并发无处不在。因为并发,所以互斥。
资损资损防控的系统规范-收单类服务设计
📫 作者简介:小明java问道之路,专注于研究 Java/ Liunx内核/ C++及汇编/计算机底层原理/源码,就职于大型金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。
📫 热衷分享,喜欢原创~ 关注我会给你带来一些不一样的认知和成长。
🏆 InfoQ签约作者、CSDN专家博主/后端领域优质创作者/内容合伙人、阿里云专家/签约博主、51CTO专家 🏆
🔥 如果此文还不错的话,还请👍关注 、点赞 、收藏三连支持👍一下博主~
本文目录
7、对外服务接口规范需要遵从最新的“文档规范说明.docx”
本文导读
大型互联网金融公司是如何保证资金万无一失的?本文从系统架构层面整体的资损防控的规范,详细介绍收单类服务资损防控需要的注意事项。
一、 资损防控系统设计资损防控规范
从系统架构层面整体来看,支付公司的系统可以抽象为如下结构:
一、对外部商户提供收单服务类的系统
二、连通支付公司与各金融渠道的网关类系统
三、支付公司的内部业务处理系统
四、消息、调度等中间件系统
五、数据库、缓存等存储平台
从系统架构与业务架构上来讲,各个结构连接的地方最容易出现资损。因此我们将从接口服务层面与系统设计层面对资损进行分析并总结相关规范。
二、服务接口类设计概述
按照上述系统抽象架构所示,支付公司接口服务可以分为三类服务:
1、渠道网关类服务
2、收单类服务
3、内部系统接口服务结果。
下面按照各类服务风险又高到底进行分类介绍。
三、收单类服务常见资损风险
1、服务描述不清楚、不规范
对于商户接口类服务,服务接口、数据交互描述不清楚或不规范,导致商户错用接口,进而导致商户资损。
2、金额参数单位乱用
在收单服务接口的所有与出入参数里,金额单位的约定是极易出错的地方,分、元、忽的使用一定要明确,否则会导致金额扩大或缩小100、1000000倍。
3、幂等参数不明确、不合理
收单服务的幂等性控制参数一定要明确指出,否则容易造成商户。如接口调用时,对同一笔业务进行多次服务调用,导致业务重复处理,造成资损;支付公司结果通知(同步或异步)时,商户没有幂等处理,对同一笔也处理多次导致资损。
4、接口参数长度或类型不明确
收单服务对外出入参数中,各参数的类型长度约定不明确,将引发数据库超长、数据转换与截断等问题,严重时将导致资损。
5、请求参数合法性校验不严密或未校验
对外提供的接口,没有对参数进行合法性校验或缺乏明确的校验规则。如收到外部请求,传入未签约的支付方式,没有签订手续费费率,可能导致漏收手续费。
6、接口的错误码或返回码定义不明确
错误码、返回码对于异常、超时、幂等等结果不明确,会导致商户重复针对同一笔业务进行多次调用或将可以判定为成功的业务而判断为失败。
另外,外部响应结果如果包含多层状态(通讯状态、业务状态等),需要明确业务状态是由哪个状态或者哪几个状态组合决定,判断错误会导致严重资损。
四、收单服务接口设计规范
1、接口描述规范
接口服务能力描述简洁明了,对于服务能力、功能、数据交互有一个清晰的描述。关键的资金处理业务需要由流程图表达。
2、金额单位规范 金额参数必须以“元”为单位
金额单位规范 金额参数必须以“元”为单位,同时精度要求小数点后两位。禁止以分或厘为单位(线下收单行业规范可以以分为单位)。
3、幂等控制
幂等控制需要约定好幂等控制的参数是哪个参数(如外部交易单号)或哪些参数(如交易日期+交易流水号)组成,对于幂等控制的方式双方需要达成一致。
4、收单服务的参数类型与长度必须明确且设计合理
任何一个参数都要明确其类型与数据长度范围,该范围需要确保在个体系内有效。 另外,对于关键的控制类参数(如幂等控制的参数)需要设计为字符串,减少不必要的转换。
5、对于商户的接口权限与接口参数做好权限控制与合法性校验
对于商户的接口权限与接口参数做好权限控制与合法性校验。
6、对于错误码或返回码需要清晰定义
返回的结果中需要明确成功失败状态的含义:业务成功,操作成功,双成功。明确错误码的含义,准确识别出成功、失败、处理中、超时处理、重复处理、不做任何等重要处理结果;对于各种异常处理(如超时重发等),不能缺少。
7、对外服务接口规范需要遵从最新的“文档规范说明.docx”
对外服务接口规范需要遵从最新的“文档规范说明.docx”。
总结
大型互联网金融公司是如何保证资金万无一失的?本文从系统架构层面整体的资损防控的规范,详细介绍收单类服务资损防控需要的注意事项。
以上是关于资损分布式系统并发互斥设计的主要内容,如果未能解决你的问题,请参考以下文章