远程通信|「微服务架构」微服务集成中的3个常见缺陷-以及如何避免它们( 三 )


3.分布式交易很难
远程通信|「微服务架构」微服务集成中的3个常见缺陷-以及如何避免它们文章插图
事务是以全有或全无的方式执行的一系列操作 。 我们都从数据库中知道这一点 。 您开始一个事务 , 做一些事情 , 然后提交或回滚事务 。 这些事务称为ACID:原子 , 一致 , 隔离和持久 。
在分布式系统中 , 您不能指望ACID事务 。 是的 , 有像XA这样的协议实现了所谓的两阶段提交 。 或WS-AtomicTransaction 。 或像Google Spanner这样复杂的实施 。 但目前的共识是 , 这些协议太昂贵 , 太复杂 , 或者根本无法扩展 。Pat Helland的“超越分布式交易的生活:Apostate的意见”是一个很好的背景阅读 。
但当然 , 商业交易的要求并没有消失 。 在没有ACID的情况下解决业务交易的常见技巧是使用补偿 。 这意味着您可以对过去不正确执行的所有活动执行撤消活动 。BPMN具有此内置功能 , 因此您可以定义这些撤消活动 , 并且工作流引擎负责以正确的顺序可靠地执行它们 。 这次我将使用预订机票的例子:
远程通信|「微服务架构」微服务集成中的3个常见缺陷-以及如何避免它们文章插图
这通常也被称为Saga模式 , 最近变得非常流行 。 我在“Saga:如何在没有两阶段提交的情况下实现复杂的业务交易”中写到了这一点 , 其中我还链接了其他来源和一些代码 。
请注意 , 此方法与ACID事务不同 , 因为您可以具有不一致的中间状态 。 所以 , 我可以保留一个座位 , 但尚未预订有效的机票 。 或者我可以在没有付款的情况下买票 。 实际情况是 , 只要确保最终清理它们并使系统恢复到一致状态 , 通常可以忍受这些暂时的不一致 。 这称为最终一致性 , 这是分布式系统中的一个重要概念 。“在SoA网络中拥抱最终的一致性”指出它非常好:
最终的一致性通常会产生更好的性能 , 更简单的操作和更好的可伸缩性 , 同时要求程序员理解更复杂的数据模型 。
好消息是工作流程自动化简化了补偿的处理 。 这是因为工作流引擎可以可靠地调用所有必要的补偿活动 。
服务提供商 - 做好功课!
到目前为止 , 我已经提出了三种简单的补救措施来应对分布式系

  1. 重试
  2. 超时
  3. 赔偿金
所有这些都可以使用轻量级工作流自动化技术实现 。 但是为了利用这些配方 , 每个服务提供商都必须做好功课 。 这意味着
  1. 提供补偿活动和
  2. 实现幂等性 。

远程通信|「微服务架构」微服务集成中的3个常见缺陷-以及如何避免它们文章插图
虽然第一个要求应该是显而易见的(如果有取消票证的服务 , 我只能取消票证) , 第二个 - 幂等性 - 需要更多解释 。
幂等
我谈了很多关于重试的事情 。 一个常见的问题是 , 如果我通过重试两次调用服务怎么办?这个问题问得好!
首先要确保您了解每种形式的远程通信都会遇到此问题!无论何时通过网络进行通信 , 都无法区分三种故障情形:
  1. 该请求尚未到达提供商
  2. 请求已到达提供商 , 但在处理期间它已爆炸
  3. 提供程序处理了请求 , 但响应丢失了

远程通信|「微服务架构」微服务集成中的3个常见缺陷-以及如何避免它们文章插图
一种可能性是询问服务提供商是否已经看到此请求 。 但更常见的方法是使用重试并以允许重复调用的方式实现服务提供程序 。 这更容易设置 。
我看到两种简单的方法来掌握幂等性:
  1. 自然的幂等性 。 有些方法可以随意执行 , 因为它们只是翻转一些状态 。 示例:confirmCustomer()
  2. 商业幂等 。 有时 , 您拥有允许您检测重复呼叫的业务标识符 。 示例:createCustomer(email)
如果这些方法不起作用 , 您需要添加自己的幂等性处理:
  1. 唯一身份 。 您可以生成唯一标识符并将其添加到呼叫中 。 这样 , 如果您在服务提供商端存储该ID , 则可以轻松发现重复呼叫 。 如果您利用工作流引擎 , 您可能会让它完成繁重的工作(例如 , 当Camunda允许在启动期间对密钥进行重复检查时) 。 示例:charge(transactionId , amount)
  2. 请求哈希 。 如果您使用消息传递 , 则可以通过存储消息的哈希值来执行相同的操作 。 您可以再次利用工作流引擎 , 或者您可以使用具有内置租赁功能的数据库(如Redis) 。
长话短说:在您的服务中注意幂等性 。 这将带来巨大的回报 。
给我看一下代码您可以使用BPMN和开源Camunda引擎找到实现我在此描述的模式的源代码