微服务是不是可以结合 REST 和消息传递?
Posted
技术标签:
【中文标题】微服务是不是可以结合 REST 和消息传递?【英文标题】:Is it possible to combine REST and messaging for microservices?微服务是否可以结合 REST 和消息传递? 【发布时间】:2019-10-26 00:57:56 【问题描述】:我们拥有基于微服务架构的应用程序的第一个版本。我们使用 REST 进行外部和内部通信。
现在我们要从 CP(CAP 定理)* 切换到 AP,并使用消息总线进行微服务之间的通信。 有很多关于如何基于Kafka、RabbitMQ等创建事件总线的资料。 但是我找不到 REST 和消息传递组合的任何最佳实践。 例如,您创建了一个汽车服务,您需要添加不同的汽车组件。为此,将 REST 与 POST 请求一起使用会更有意义。另一方面,预订汽车的服务对于基于事件的方法来说将是一项很好的任务。
当您拥有不同的字典和业务逻辑功能时,您是否有类似的方法?你如何将它们结合起来?只是分别支持这两种方法?还是用一种方法统一它们?
* 对于第一个版本,我们同意选择一致性和分区容差。但现在可用性对我们来说变得更加重要。
【问题讨论】:
我觉得这个问题很难理解。你的意思是“AP from CP”而不是“AP form CP”?您能否改进这句话:“我看不出有任何理由使用事件 (..) 但为基于事件的方法创建汽车服务预订好任务。” “你如何结合它们”是什么意思?是否要同时实现 REST-API 和非 REST-API? @www.admiralalit.nl 感谢您的评论。我已经编辑了。 如果您将 CQRS(命令查询职责分离)视为事件驱动(命令)和基于 REST(查询)之间的拆分,那么您会看到不同的操作如何使用不同的通信方法。我不会在给定的操作中同时使用这两种协议,因为 REST 是一种阻塞协议,而事件本质上是非阻塞和异步的。 在微服务之间,你永远不会使用 Post 请求。就像有人提到的 REST/HTTP 是阻塞协议。所以你不能很好地扩展。通常你会使用 SAGA 模式。您在本地提交事务并在消息代理上发出事件。该事件将被其他服务消费。如果有任何问题,您需要提出有偿交易/事件。您还可以在本地保存事件,然后再发布它们调用发件箱模式。因此,如果服务总线不可用,您不会丢失事件microservices.io/patterns/data/saga.html 【参考方案1】:前面的底线:您正在寻找Command Query Responsibility Segregation;它定义了一种架构模式,用于分解从查询数据到请求运行流程的职责。简短的回答是您不想在查询或进程中以阻塞方式混合两者。这个答案的其余部分将详细说明原因,以及您可以做您想做的事情的三种不同方式。
这个答案是我在微服务方面的经验的简短形式。我的诚意:我从头开始创建了微服务拓扑(并且知识几乎为零),正如他们所说,在下降的过程中触及了每个分支。
从零知识开始的一个好处是,我创建的第一个拓扑混合使用了服务内同步和阻塞 (HTTP) 通信(从持有它的服务检索操作所需的数据),和消息队列 + 异步事件来运行操作(对于命令)。
我将定义这两个术语:
命令:告诉服务做某事。例如,“运行 ETL 批处理作业”。你期望有一个输出;但这必然是一个您无法可靠等待的过程。命令有副作用。由于此操作,某些事情会发生变化(如果没有发生任何事情并且没有任何变化,那么您什么都没有做)。
查询:向服务询问其持有的数据。由于给出了命令,这些数据可能已经存在,但要求数据不应该有副作用。由于收到了查询,因此不应需要运行任何命令操作。
不管怎样,回到拓扑。
级别 1:混合 HTTP 和事件
对于第一个拓扑,我们将同步查询与发出的异步事件混合在一起。这是……有问题的。
消息总线本质上是可观察的。 RabbitMQ 中的一项设置,或者一个事件源,您可以观察系统中的所有事件。这有一些很好的副作用,因为当过程中发生某些事情时,您通常可以找出导致该状态的事件(如果您遵循事件驱动的范例 + 状态机)。
如果不检查网络流量或记录这些请求,就无法观察到 HTTP 调用(这本身就有问题,所以我们将从正常操作中的“不可行”开始)。因此,如果您将基于消息的进程和 HTTP 调用混合在一起,您将有无法判断发生了什么的漏洞。由于网络错误,您的 HTTP 调用没有返回数据,您的服务因此没有继续该过程。您还需要为您的 HTTP 调用连接 Retry/Circuit Breaker 模式,以确保它们至少尝试几次,但是您必须区分“因为它关闭而未启动”和“因为它暂时繁忙而未启动” ”。
简而言之,将这两种方法混合用于命令驱动进程并不是很有弹性。
级别 2:事件定义数据的 RPC/内部请求/响应;查询是外部的
在此成熟度模型的第二步中,您将命令和查询分开。命令应该使用事件驱动系统,查询应该通过 HTTP 进行。如果您需要命令查询的结果,那么您可以发出一条消息并在您的消息总线上使用请求/响应模式。
这既有好处也有问题。
在好处方面,您的整个命令现在是可观察的,即使它通过多个服务跳跃。您还可以通过重新运行事件来重放系统中的进程,这对于跟踪问题很有用。
问题方面,现在您的一些事件看起来很像查询;现在,您正在为消息重新创建 HTTP 中可用的漂亮 HTTP 和 REST 语义;这不是非常有趣或有用。例如,404 告诉您 REST 中没有数据。对于基于消息的事件,您必须重新创建这些语义(有一个关于这个主题的很好的 Youtube 会议演讲我找不到,但是一个团队试图这样做很痛苦)。
但是,您的事件现在是异步且非阻塞的,并且每个服务都可以重构为响应给定事件的状态机。需要注意的是,这些事件应该包含操作所需的所有数据(这会导致消息在整个过程中不断增长)。
您的查询仍然可以使用 HTTP 进行外部通信;但对于内部命令/进程,您将使用消息总线。
我也不推荐这种方法(尽管它比第一种方法更先进)。我不推荐它,因为您的事件开始产生杂质,并且在微服务系统中,整个系统的合约相同是很重要的。
级别 3:数据的生产者将数据作为事件发出。消费者记录数据以供使用。
成熟度模型的第三步(当我离开该项目时,我们正朝着那个范式迈进)是为产生数据的服务在产生数据时发出事件。然后,监听这些事件的服务会记下这些数据,这些服务将使用这些(可能是?)陈旧的数据来执行它们的操作。外部客户仍然使用 HTTP;但是在内部,当产生新数据时,您会发出事件,并且每个关心该数据的服务都会将其存储起来,以便在需要时使用。这是 Michael Bryzek 谈话Designing Microservices Architecture the Right way 的关键。 Michael Bryzek 是Flow.io 的首席技术官,这是一家白标电子商务公司。
如果您想获得更深入的答案以及其他问题,我会告诉您my blog post on the subject。
【讨论】:
感谢您提供如此详细的回答。在讨论中,我们决定使用类似于第 3 级的东西。感谢您的链接,它应该对我们有所帮助。 当您说“外部客户仍在使用 HTTP;但在内部您会在生成新数据时发出事件”时,我很好奇这会对单个服务的体系结构产生什么影响。例如。听起来许多服务需要同时监听 HTTP 请求(以支持其余端点)以及监听来自某些消息服务(例如 redis)的事件。这是否意味着您需要为此服务运行两个进程(例如,一个传统的 REST api,以及一个监听事件总线的单独进程),或者有没有办法将两者集成到一个中? @G.Shand 你可以使用同样的服务;并且有效地有两个“端点”;消息队列监听器;和一个 HTTP API 端点;并且最终都在内部调用相同的方法以使某些事情发生。这里的重点是将服务的功能与其所服务的交付机制分离。以上是关于微服务是不是可以结合 REST 和消息传递?的主要内容,如果未能解决你的问题,请参考以下文章
精通springcloud:消息驱动的微服务,了解Spring Cloud Stream