生产案例-解冻支付功能-分布式数据一致性(分布式事务) 这是一个银行资金监管系统生产中的真实案例我方资金监管系统A在完成资金支付功能时下游涉及两个系统。需要先调用系统B的解冻接口完成资金解冻然后再调用系统C的转账接口完成资金支付。各系统间数据一致性保障采用类似SAGA事务的思路如果失败或异常我方统一负责补偿或冲正处理。系统B提供资金解冻接口、资金冻结接口、执行结果查询接口通过上送唯一流水号保证幂等和查询结果条件。系统C提供资金转账接口、转账结果查询接口通过上送唯一流水号保证幂等和查询结果条件。数据库表1支付流水表(支付流水号解冻流水号冻结流水号支付状态待处理、处理中、成功、失败、不确定解冻状态待处理、处理中、成功、失败、不确定冻结状态待处理、处理中、成功、失败、不确定金额付款账户收款账户交易日期各种时间版本号等)2冻结解冻记录表(流水号支付流水号类型状态账号金额时间结果)3支付执行防重表(支付流水号[唯一约束])账户主要结构字段总余额、业务冻结额、可用余额总余额业务冻结额可用余额。账户纳管后,来账资金都会被冻结到业务冻结额中属于被监管资金。概要业务逻辑1我方系统A做支付前置校验2) 调用资金解冻接口解冻付款账户资金(支付金额)。(资金从业务冻结额释放到可用余额中)3调用资金转账接口发起转账。从账号可用余额中转账4支付结果处理接口返回成功/失败/其他则更新支付状态成功/失败/不确定5如果最终支付失败(终态)调用资金冻结接口重新冻结资金。资金从可用余额冻结到业务冻结额中支付简化伪代码1)幂等控制方法 分布式锁(可选) 检查支付状态待处理 插入支付执行防重表(保障一笔支付申请至多执行一次付款) 版本号做为条件CAS更新支付流水表支付状态处理中执行时间? //CAS为比较交换操作保证并发原子性执行sql后判断影响行数不等于1则抛异常 //所有更新操作版本号默认1 2业务检查方法 try{ 业务检查 }catch(e){ 更新支付流水表支付状态失败失败原因? throw e; } 3付款账户资金解冻方法 try{ 版本号CAS更新支付流水表解冻状态处理中解冻流水号? 记录冻结解冻记录表 调用资金解冻接口解冻资金(待支付金额)。 如果返回成功更新支付流水表解冻状态为成功。 如果返回失败更新支付状态设置为失败解冻状态为失败return。 如果返回其他(可能是处理中/不确定/超时等)抛异常。 }catch(e){ 支付状态更新为失败解冻状态更新为不确定。 【异步补偿定时任务通过执行结果查询接口查询解冻结果】 throw e; } 4账户转账方法 try{ 调用转账接口。 如果返回成功更新支付状态为成功。 如果返回失败更新支付状态设置为失败执行冻结冲正方法。 如果返回其他抛异常。 }catch(e){ 更新支付流水表支付状态不确定 【异步补偿定时任务通过转账结果查询接口查询结果】 throw e; }冻结冲正简化伪代码try{ 查询是否解冻成功 版本号CAS更新支付流水表冻结状态处理中冻结流水号? where 冻结状态 in (待处理/失败) //所有更新操作版本号默认1 记录冻结解冻记录表 调用资金冻结接口冻结资金(支付金额)。 如果返回成功更新支付流水表冻结状态为成功。 如果返回失败冻结状态为失败return。【对于冻结冲正失败的会有异步定时任务做补偿重试/短信告警】 如果返回其他(可能是处理中/不确定/超时等)抛异常。 }catch(e){ 冻结状态更新为不确定。 【异步补偿定时任务通过执行结果查询接口查询冻结结果更新冻结状态】 }补偿定时任务(异步化)核心补偿场景1:资金解冻成功但是付款失败/没执行付款# 支付状态不确定。查询支付结果更新支付状态,并更新。 # 解冻状态不确定。查询解冻结果更新解冻状态,并更新。 # 冻结状态不确定。查询冻结结果更新冻结状态,并更新。 # 冻结状态处理中执行时间1小时。查询冻结结果更新冻结状态,并更新。 # 冻结状态失败。重试或短信告警人工处理。 # 解冻状态成功支付状态失败冻结状态待处理。做冻结冲正处理重新冻结账户资金。 # 支付状态处理中执行时间10分钟。按照支付状态、解冻状态不同情况做逐一处理。调用各查询接口确定下游系统执行状态监控定时任务(异步化)定时检查账户金额如果可用余额分行设置的阈值则报警。