伪代码
场景
用购买某种商品1件(原库存1000),增加20积分,扣除100元余额
设计说明
通过日志表来完成TCC。修改库存,余额表要先添加一条记录到他们的日志表里,表要记录TCC状态。
通过唯一业务Code去重,保证幂等。
没有抽象一个活动管理器来管理TCC。
服务
本地创建订单服务createOrder 本地插入订单Mapper层 insertOrder(orderCode) 本地其他服务 远程服务是否可以扣除库存ifReduceStock(orderCode,reduceNumber) 远程服务是否可以扣除余额ifReduceBalance(orderCode,sum) 远程服务Try扣除库存,锁住扣除库存tryReduceStock(orderCode,reduceNumber) 远程服务Try扣除余额,锁住扣除余额tryReduceBalance(orderCode,sum) 远程服务增加积分 远程服务Confirm扣除库存confirmReduceStock(orderCode,ReduceNumber) 远程服务Confirm扣除余额ConfirmReduceBalance(orderCode,sum) 远程服务Cancel扣除库存cancelReduceStock(orderCode,ReduceNumber) 远程服务Cancel扣除余额cancelReduceBalance(orderCode,sum) 远程服务增加积分 远程其他服务
补充服务:扣除日志超过timeOut时间的记录状态设置为失效Cancel(在是否扣除ifReduce,Confirm操作中调用补充服务)
本地创建订单服务createOrder描述
触发:用户点击下单
本地业务服务事务开始 本地业务服务同步检查(调用远程服务是否可以扣除库存ifReduceStock(orderCode,reduceNumber), 远程服务是否可以扣除余额ifReduceBalance(orderCode,sum),其他)
调用远程服务是否可以扣除库存ifReduceStock(orderCode,reduceNumber)说明:
首先,要调用补充服务把日志中超过timeOut时间的记录状态设置为失效Cancel。
其次,要把日志状态为锁住Try库存从剩余库存剪掉
余额同上 生成订单Code(orderCode全世界唯一) 同步本地业务插入订单Mapper层 insertOrder(orderCode),得到订单 其他本地业务
同步调用远程服务Try扣除库存tryReduceStock(orderCode,reduceNumber),锁住扣除库存1
具体:
远程添加此次扣除日志(三种方式:数据库,Redis,MongoDB),其他判断是否可以扣除库存务必减去这个库存1(从扣除日志找orderCode相同,状态为锁住Try的操作库存数)
数据记录主要字段,值:
orderCode(用于去重)
原库存,值1000
此次操作库存,值1
操作时间(一个用途,用于计算超过某个值timeOut后,把此条记录状态设置为失效Cancel)
状态(锁住Try,确认Confirm,失效Cancel),值锁住Try
同步调用远程服务Try扣余额tryReduceBalance(orderCode,sum),锁住扣除余额100
具体同库存
同步调用远程服务Try增加积分
具体同库存(比库存简单一些) 有任何错误或者异常回滚本地事务 同步调用远程服务Confirm扣除库存confirmReduceStock(orderCode,ReduceNumber)
具体:
首先,要调用补充服务把日志中超过timeOut时间的记录状态设置为失效Cancel
其次,扣除日志里找到orderCode,此次操作库存为ReduceNumber1,状态为锁住Try记录,根据此条记录扣减库存。 调用远程扣除库存错误或者失败回滚本地事务 同步调用远程服务Confirm扣除余额ConfirmReduceBalance(orderCode,sum) 远程扣除余额调用错误或者失败回滚本地事务,远程调用服务Cancel扣除库存cancelReduceStock(orderCode,ReduceNumber)还原库存
具体还原库存:
除日志里找到orderCode,此次操作库存为ReduceNumber1,状态为确认Confirm记录,根据此条记录还原库存。 同步远程调用增加积分如果失败同样要还原库存和余额 本地业务服务事务结束,提交
问题
1.有些地方为什么没有进行Cancel操作?因为超过TimeOut会Cancel。
2.秒杀活动等高并发怎么办?秒杀情况下,应在下单前的购买和加入购物车操作做限制。添加一个队列,队列中请求已把库存买光,就不允许进入队列走下单。只有进入队列的才可以下单。使用Redis实现。