Content #
当接收到外部指令时,需要实现操作 1、2、3,其中任何一个操作失败,都需要暂停指令执行,将系统恢复到操作未执行状态,然后再重试。
其中,操作 1、2、3 的含义具体如下。
- 操作 1:生成指定 URL 页面对应的图片,并持久化存储。
- 操作 2:调用内部系统 1 的接口,禁用指定域名的访问权限。
- 操作 3:通过 MySQL XA 更新多个数据库的数据记录。
那么我是如何使用 TCC 来解决这个问题的呢?
答案是我在实现每个操作时,都会分别实现相应的预留、确认、撤销三操作。
首先,因为操作 1 是生成指定 URL 页面对应的图片,可以这么实现 TCC 三操作。
- 预留操作:生成指定页面的图片,并存储到本地。
- 确认操作:更新操作 1 状态为完成。
- 撤销操作:删除本地存储的图片。
其次,因为操作 2 是调用内部系统 1 的接口,禁用该域名的访问权限,那么,我是这么实现 TCC 三操作的。
- 预留操作:调用的内部系统 1 的禁用指定域名的预留接口。这时我们先通知系统 1 预留相关的资源。
- 确认操作:调用的内部系统 1 的禁用指定域名的确认接口。我们执行禁用域名的操作,这时,禁用域名的操作的生效了。
- 撤销操作:调用的内部系统 1 的禁用指定域名的撤销接口。我们撤销对该域名的禁用,并通知内部系统 1 释放相关的预留资源。
最后,操作 3 是通过 MySQL XA 更改多个 MySQL 数据库中的数据记录,并实现数据更新的事务。我是这么实现 TCC 三操作的:
- 预留操作:执行 XA START 和 XA END 准备好事务分支操作,并调用 XA PREPARE,执行二阶段提交协议的提交请求阶段,预留相关资源。
- 确认操作:调用 XA COMMIT 执行确认操作。
- 撤销操作:调用 XA ROLLBACK 执行回滚操作,释放在 Try 阶段预留的资源。
可以看到,确认操作是预留操作的下一个操作,而撤销操作则是用来撤销已执行的预留操作对系统产生的影响,类似在复制粘贴时,我们通过“Ctrl Z”撤销“Ctrl V”操作的执行,就像下图的样子。而这是理解 TCC 的关键。
在操作 1、2、3 的预留操作执行结束,如果预留操作都执行成功了,那么我将执行确认操作,继续向下执行。但如果预留操作只是部分执行成功,那么我将执行撤销操作,取消预留操作执行对系统产生的影响。通过这种方式(指令对应的操作要么全部执行,要么全部不执行),我就能实现指令执行的原子性了。
另外,在实现确认、撤销操作时,有一点需要我们尤为注意,因为这两个操作在执行时可能会重试,所以,它们需要支持幂等性。