是否可以将 REST 和消息传递结合起来用于微服务?

Is it possible to combine REST and messaging for microservices?

我们拥有基于微服务架构的应用程序的第一个版本。我们使用 REST 进行外部和内部通信。

现在我们想从 CP(CAP 定理)* 切换到 AP,并使用消息总线进行微服务之间的通信。 关于如何基于 Kafka、RabbitMQ 等创建事件总线的资料很多。 但是我找不到任何结合 REST 和消息传递的最佳实践。 例如,您创建一个汽车服务,您需要添加不同的汽车组件。为此,将 REST 与 POST 请求一起使用会更有意义。另一方面,预订汽车的服务对于基于事件的方法来说是一项很好的任务。

当你有不同的字典和业务逻辑能力时,你有类似的方法吗?你如何结合它们?只是分别支持这两种方法?或者用一种方法统一它们?

* 对于第一个版本,我们同意选择一致性和分区容忍度。但现在可用性对我们来说变得更加重要。

前面的底线:您正在寻找 Command Query Responsibility Segregation;它定义了一种架构模式,用于分解从查询数据到请求流程的职责 运行。简短的回答是您不希望以阻塞方式在查询或进程中混合这两者。这个答案的其余部分将详细说明原因,以及您可以做您想做的事情的三种不同方式。

这个答案是我在微服务方面的经验的简短形式。我的诚意:我从头开始创建了微服务拓扑(几乎是零知识),正如他们所说,在下降的过程中触及了每个分支。

从 zero-knowledge 开始的好处之一是我创建的第一个拓扑混合使用了 intra-service 同步和阻塞 (HTTP) 通信(从持有它的服务)和消息队列 + 异步事件到 运行 操作(对于命令)。

我将定义这两个术语:

命令:告诉服务做某事。例如,"Run ETL Batch job"。您希望从中得到输出;但这必然是一个您无法可靠地等待的过程。一个命令有 side-effects。某些事情会因为这个动作而改变(如果什么都没有发生,什么也没有改变,那么你什么都没做)。

查询:向服务请求其持有的数据。该数据可能是由于给出的命令而存在的,但是请求数据 不应该有副作用 。没有命令操作应该需要是运行因为收到查询。

总之,回到拓扑结构。

第 1 级:混合 HTTP 和事件

对于第一个拓扑,我们将同步查询与发出的异步事件混合在一起。这……有问题。

消息总线本质上是可观察的。 RabbitMQ中的一个设置,或者一个Event Source,你可以观察到系统中的所有事件。这有一些好处 side-effects,因为当过程中发生某些事情时,您通常可以找出导致该状态的事件(如果您遵循 event-driven 范式 + 状态机)。

在不检查网络流量或记录这些请求的情况下无法观察到 HTTP 调用(这本身就有问题,因此我们将从正常操作中的 "not feasible" 开始)。因此,如果您将基于消息的流程和 HTTP 调用混合在一起,就会出现无法判断发生了什么的漏洞。由于网络错误,您会遇到一些地方您的 HTTP 调用没有 return 数据,并且您的服务因此没有继续该过程。您还需要为 HTTP 调用连接 Retry/Circuit 断路器模式,以确保它们至少尝试几次,但是您必须区分 "Not up because it's down" 和 "Not up because it's momentarily busy"。

简而言之,为命令驱动流程混合使用这两种方法不是很有弹性。

2级:事件为数据定义RPC/InternalRequest/Response;查询是外部的

在此成熟度模型的第二步中,您将命令和查询分开。命令应使用事件驱动系统,查询应通过 HTTP 进行。如果您需要命令的查询结果,那么您可以发出消息并在消息总线上使用 Request/Response 模式。

这既有好处也有问题。

Benefits-wise 您的整个命令现在是可观察的,即使它在多个服务之间跳跃。您还可以通过重新运行 事件重放系统中的进程,这对于跟踪问题很有用。

Problems-wise 现在您的一些事件看起来很像查询;您现在正在为消息重新创建 HTTP 中可用的漂亮 HTTP 和 REST 语义;这不是很有趣或有用。例如,404 告诉您 REST 中没有数据。对于基于消息的事件,您必须重新创建这些语义(有一个关于这个主题的很好的 Youtube 会议演讲我找不到,但是一个团队试图做到这一点非常痛苦)。

但是,您的事件现在是异步的 non-blocking,并且每个服务都可以重构为 state-machine 来响应给定的事件。一些注意事项是那些事件应包含操作所需的所有数据(这会导致消息在流程过程中不断增长)。

您的查询仍然可以使用 HTTP 进行外部通信;但对于内部 command/processes,您将使用消息总线。

我也不推荐这种方法(尽管它比第一种方法更高级)。我不推荐它,因为你的事件开始变得不纯,并且在微服务系统中,整个系统的合同都是相同的很重要。

级别 3:数据生产者将数据作为事件发出。消费者记录数据供他们使用。

成熟度模型的第三步(当我离开该项目时,我们正在走向那个范式)是针对产生数据以发布的服务生成该数据时的事件。然后,监听这些事件的服务会记下这些数据,这些服务将使用这些(可能是?)过时的数据来执行它们的操作。外部客户仍然使用 HTTP;但在内部,当产生新数据时,您会发出事件,并且每个关心该数据的服务都会存储它以在需要时使用。这是 Michael Bryzek 演讲的关键 Designing Microservices Architecture the Right way. Michael Bryzek is the CTO of Flow.io,一家 white-label e-commerce 公司。

如果您想要更深入的答案以及其他正在发生的问题,我会向您指出 my blog post on the subject