TCC 强一致性 实时 DEMO 下单(创建订单,扣除库存,增加积分,扣除余额)
伪代码
场景
用购买某种商品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描述
图1:创建订单-活动图
触发:用户点击下单
本地,检查
- 本地业务服务事务开始
- 本地业务服务同步检查(调用远程服务是否可以扣除库存ifReduceStock(orderCode,reduceNumber), 远程服务是否可以扣除余额ifReduceBalance(orderCode,sum),其他)
调用远程服务是否可以扣除库存ifReduceStock(orderCode,reduceNumber)说明:
首先,要调用补充服务把日志中超过timeOut时间的记录状态设置为失效Cancel。
其次,要把日志状态为锁住Try库存从剩余库存剪掉
余额同上 - 生成订单Code(orderCode全世界唯一)
- 同步本地业务插入订单Mapper层 insertOrder(orderCode),得到订单
- 其他本地业务
Try
同步调用远程服务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,Cancel
- 同步调用远程服务Confirm真正扣除库存confirmReduceStock(orderCode,ReduceNumber)
具体:
首先,要调用补充服务把日志中超过timeOut时间的记录状态设置为失效Cancel
其次,扣除日志里找到orderCode,此次操作库存为ReduceNumber1,状态为锁住Try记录,根据此条记录扣减库存。 - 失败了。1)Cancel扣除库存,取消占有资源2)回滚本地事务。结束。
- 同步调用远程服务Confirm扣除余额ConfirmReduceBalance(orderCode,sum)
- 失败了。
1)Cancel扣除余额,取消占有资源
2)远程调用服务Cancel扣除库存cancelReduceStock(orderCode,ReduceNumber)还原库存
具体还原库存:
除日志里找到orderCode,此次操作库存为ReduceNumber1,状态为确认Confirm记录,根据此条记录还原库存。
3)回滚本地事务。结束。 - 同步远程调用增加积分如果失败同样要还原库存和余额
- 本地业务服务事务结束,提交。
问题
1.为什么有些地方可以不进行Cancel操作?因为超过TimeOut会Cancel。
2.秒杀活动等高并发怎么办?秒杀情况下,应在下单前的购买和加入购物车操作做限制。添加一个队列,队列中请求已把库存买光,就不允许进入队列走下单。只有进入队列的才可以下单。使用Redis实现。