忘川彼岸|我只是下了个订单,鬼知道我在微服务里经历了什么?( 六 )


完美 , 使用 MQ 分布式事务就可以解决调一致性问题 。
忘川彼岸|我只是下了个订单,鬼知道我在微服务里经历了什么?等等 , MQ 消息事务方案的风险了解一下 。
上面使用 MQ 的方式确实是可以完成 A 和 B 操作 , 但是 A 和 B 并不是严格一致性 , 而是最终一致性 。
我们牺牲掉严格一致性 , 换来性能的提升 , 这种很适合在大促高并发场景使用 。
但是如果 B 一直执行不成功 , 那么一致性也会被破坏 , 后续应该考虑到更多的兜底方案 , 方案越细系统就将越复杂 。
TCC 方案
TCC 是服务化的二阶段变成模型 , 每个业务服务都必须实现 Try , Confirm , Calcel 三个方法 。
这三个方式可以对应到 SQL 事务中 Lock , Commit , Rollback:

  • Try 阶段:Try 只是一个初步的操作 , 进行初步的确认 , 它的主要职责是完成所有业务的检查 , 预留业务资源 。
  • Confirm 阶段:Confirm 是在 Try 阶段检查执行完毕后 , 继续执行的确认操作 , 必须满足幂等性操作 , 如果 Confirm 中执行失败 , 会有事务协调器触发不断的执行 , 直到满足为止 。
  • Cancel:是取消执行 , 在 Try 没通过并释放掉 Try 阶段预留的资源 , 也必须满足幂等性 , 跟 Confirm 一样有可能被不断执行 。
接下来看看 , 我们的下单扣减库存的流程怎么加入 TCC:
忘川彼岸|我只是下了个订单,鬼知道我在微服务里经历了什么?在 Try 的时候 , 会让库存服务预留 n 个库存给这个订单使用 , 让订单服务产生一个“未确认”订单 , 同时产生这两个预留的资源 。
在 Confirm 的时候 , 会使用在 Try 预留的资源 , 在 TCC 事务机制中认为 , 如果在 Try 阶段能正常预留的资源 , 那么在 Confirm 一定能完整的提交:
忘川彼岸|我只是下了个订单,鬼知道我在微服务里经历了什么?在 Try 的时候 , 有任务一方为执行失败 , 则会执行 Cancel 的接口操作 , 将在 Try 阶段预留的资源进行释放 。
完美 , 可以把我们的系统引入 TCC 。 ^?_?^
忘川彼岸|我只是下了个订单,鬼知道我在微服务里经历了什么?等等 , 有同学提问:
  • 有同学可能会问了 , 如果在 Confirm 或 Cancel 中 , 有一方的操作失败了 , 可能出现异常等情况该怎么解决 。
这个就涉及 TCC 的事务协调器了 , 事务协调器就 Confirm 或 Cancel 没有得到返回的时候 , 会启用定时器不断的进行 Confirm 或 Cancel 的重试 。
这个也就是我们强调 , Confirm , Cancel 接口必须是幂等性的一个原因了 。
  • 还有同学会问了 , 为什么事务协调器知道 Confirm , 或 Cancel 没有完成 。
这个就涉及到了 TCC 也做了一张本地消息表 , 会记录一次事务 , 包括主事务 , 子事务 , 事务的完成情况都会记录在这种表中(当然未必是表 , 可能是 ZK , Redis 等等介质) , 然后启用一个定时器去检查这种表 。
  • 还有同学会问 , 事务怎么传递 , 这个就涉及使用的 TCC 的框架了 , 一般来说用的都是隐式传参的方式 。
在主事务创建的时候用隐式传参调用子事务 , 子事务包含 Try , Confirm , Cancel 都会记录到事务表里面 。
这里推荐 TCC 的开源框架使用 mengyun 的 TCC , 然后也可以其他的 , 无所谓 。