为了保证最终一致性, 在错误发生时, 沿着整个错误的传递路径进行undo操作. 对于复杂的工作流来说, 尽可能保证相互依赖的流程上数据语义的最终一致性.
问题
在分布式环境中, 大量的数据在节点间不断的传输和被修改. 为了保证整个系统的性能, 任何一个节点往往都是要求最终一致性, 而不是全局强一致性. 在最终一致性的模式下, 分布式环境中的每个点维护自己的状态机, 从全局来看, 整个系统处于不一致的状态. 但所有的节点在执行完规定操作后, 整个系统可以最终达到一致的状态(没有新数据进来的情况下)
这种设计带来的问题就是当一个操作在某个环节出问题了, 希望对数据进行回滚, 但是这个操作很可能无法成功. 因为上游的服务可能已经使用了错误的数据, 而下游服务可能还没有把错误数据写入. 回滚过程就不是简单的达到一个正确的一致性状态, 而是达到一个业务可以容忍的一致性状态
因此我们需要根据业务执行一定的补偿策略, 尽可能的回滚受到影响的数据, 及时终止错误数据的传播, 并对已经出错无法恢复的业务执行一定的补偿操作, 如根据日志进行非常重的重算, 或者等待新的正确数据覆盖旧数据等.
解决
这种设计模式是和业务紧耦合的, 一般来说, 是通过维护一个业务流.系统会对整个业务流进行状态记录, 当回滚时, 针对workflow的状态机进行回滚. 由于状态机是维护在各个节点上的, 所以回滚过程可以并发进行.
从这个描述, 我们知道, 能够执行这种操作的业务必须是能够接受最终一致性,所有操作必须是幂等的
决策
-
错误判断 一个业务是否失败了往往是难以判断的, 一种情况是服务端的exception则可以判断为立即失败. 另外一种, 如一个预约系统, 预约医生后医生因急事无法赴约, 这种业务端的失败很难被捕捉到
-
补偿机制难以被确定 如何回滚状态机, 回滚过程失败如何挽救等等都难以进行逻辑编程
-
非幂等操作 非幂等的操作无法回滚很容易理解, 如何把幂等操作转化成幂等操作是一个非常大的挑战
-
操作序列的影响 在回滚时可能又改变了很多状态, 能够单独回撤一个状态, 还是一起回滚, 回撤时的顺序是否有影响?
示例
image.png我们假设有这样的一个业务场景是某旅游服务提供商, 客户依次进行如下操作
- 订Flight 1航班, 从北京到上海
- 订Flight 2航班, 从上海到杭州
- 订Flight 3航班, 从杭州回北京
- 在上海H1酒店订一个房间
- 在杭州H2酒店订一个房间
这一系列操作在业务上是相互关联的, 但每个操作本身对于数据库来说是原子的. 由于业务的异步性和延时性, 很可能客户的所有航班和杭州的H2旅馆反馈正常使用, 但上海H1旅馆因为种种原因在2小时后返回失败信息, 或者更进一步的, 返回信息表示取消之前成功的操作.
一个服务非常好的公司, 会注意到这个操作链, 通知用户, 并根据用户的意愿对关联环节进行"回滚". 这里的"回滚"往往是基于策略的补偿式的. 如发现用户的航班购买成功, 但旅馆失败,则补偿更高级别的同区域旅馆, 旅馆成功但航班失败则补偿票价并自动取消旅馆的订房.
类似这样的业务操作, 需要能够理解用户意图, 并把多个原子操作合并成相互关联的一个大操作来面对.
同时再业务层对回滚进行策略编程, 形成基于业务的回滚机制
网友评论