如何在事件驱动的微服务架构中查询?

时间:2018-01-16 10:50:06

标签: microservices cqrs event-driven

假设以下基于CQRS架构的简单UC:

我们有一个管理商业对象的后端,让电影说。

  • 此后端由2个微服务组成:CommandManager(创建/更新/删除电影)和QueryManager(查询电影)
  • 我们有一个前端,提供用于创建新电影的网页,此操作自动引导到另一个描述电影的网页。

一种简单的方法是:

  • 网页使用表单收集电影信息并将其发送到前端。
  • 前端向CommandManager发出POST请求
  • CommandManager将新影片写入数据存储区并返回影片密钥
  • 前端使用此密钥对QueryManager进行GET
  • QueryManager使用密钥在数据存储区中查找电影并将其返回。
  • 前端使用电影信息传送页面。

好的,现在我想以更多事件驱动的方式转换这个UC。这是新的流程:

  • 网页使用表单收集电影信息并将其发送到前端。
  • 前端使用新电影信息在BUS中写一条消息
  • CommandManager侦听总线并在数据存储区中创建新电影。最终,它在BUS中发布一条新消息,指定已创建新电影。

此时,由于这种流是异步的,因此前端不再等待响应。我们如何完成此流程以便将用户转发到电影信息网页?在查询QueryManager之前,我们应该等待创建过程完成。

在一个更通用的术语中,在基于总线/事件的异步架构中,如何执行用于在网页中提供信息的Query?

3 个答案:

答案 0 :(得分:5)

除@ VoiceOfUnreason的答案外,

如果两个微服务是RESTFul,则CommandManager可以返回202 Accepted,其中包含指向将来创建的资源的链接。然后,客户端可以轮询该资源,直到服务器以200 OK响应。

另一个解决方案是CommandManager将返回202 Accepted,其链接指向command/status端点。客户端将轮询该端点,直到状态为command-processed(包括实际资源的URL)或command-failed(包括失败的描述性消息)。

可以使用Server Sent Events发送所有已处理命令的状态来扩充这些解决方案。通过这种方式,客户端可以在没有轮询的情况下得到通知。

如果客户端不知道架构是异步的,那么解决方案是使用API​​网关来阻止客户端的请求,直到上游微服务处理命令,然后用完整资源的数据进行响应。

答案 1 :(得分:2)

  

此时,由于这种流是异步的,因此前端不再等待响应。我们如何完成此流程以便将用户转发到电影信息网页?在查询QueryManager之前,我们应该等待创建过程完成。

简短回答:明确协议。

更长的答案:在这里寻找灵感的好地方是HTTP。

前端对原始服务器进行POST;结果,源服务器在队列和sends a response back上放置了一条消息。

  

通过此响应发送的表示应该描述请求的当前状态,并指向(或嵌入)状态监视器,该状态监视器可以向用户提供对请求何时将被满足的估计。

然后,客户端可以轮询端点以找出已经取得的进展。

例如,端点可能是对数据存储的查询,它查找命令管理器已处理原始命令的证据;或者它可能是一个端点,正在观看有关MovieCreated消息的总线,并根据它是否已经看到它来更改其答案。

它可能有助于澄清要查看idempotent请求处理的内容;当命令管理器从其队列中提取消息时how does it know,如果它先前已处理过该消息的副本?您的轮询端点应该能够使用相同的信息让消费者知道消息已成功处理。

答案 2 :(得分:1)

除了@Constantin Galbenu的答案之外,我想加上我的两分钱。

我强烈建议您查看名为" BFF"的微服务模式。 (Backend-For-Frontend)模式。您可以根据用例使用API​​,而无需使用厚API网关完成所有工作。例如:在您的情况下,您可以使用名为" CreateMovieBFFHandler"的API。它将从前端接收POST请求,然后这个人将与系统中的其他东西协调,如消息队列,事件等,以跟踪提交的请求的状态。 UI可能与此BFFhandler具有协议,如果响应在X秒内没有返回,则前端会将其视为失败,并且此处理程序是否能够从消息队列中获取成功处理的消息或& #34; MovieCreated"对于此密钥的事件,然后它可以发回200 OK,然后您可以将页面重定向到调用写入端,然后填充UI。

有用的链接:https://samnewman.io/patterns/architectural/bff/