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

13
假设有以下基于CQRS架构的简单UC:
我们有一个后端管理业务对象,例如电影。
该后端由2个微服务组成:CommandManager(创建/更新/删除电影)和QueryManager(查询电影)。
我们有一个前端,提供一个网页用于创建新电影,此操作自动导航到另一个描述电影的网页。
实现方式如下:
一个网页使用表单收集电影信息并将其发送到前端。
前端向CommandManager发出POST请求。
CommandManager将新电影写入数据存储并返回电影键。
前端使用该键向QueryManager发出GET请求。
QueryManager使用键在数据存储中查找电影并返回它。
前端提供带有电影信息的页面。
现在,我想以更多的事件驱动方式转换这个UC。以下是新的流程:
网页使用表单收集电影信息并将其发送到前端。
前端在总线上写入消息,其中包含新电影信息。
CommandManager监听总线并在数据存储中创建新电影。最终,它会在总线上发布一条新消息,指定已创建了新电影。
此时,前端不再等待响应,因为此类流程是异步的。我们如何完成此流程以将用户转发到电影信息网页?我们应该在查询QueryManager之前等待创建过程完成。
更一般地说,在基于总线/事件的异步架构中,如何执行用于提供网页信息的查询?
3个回答

9
除了 @VoiceOfUnreason 的答案之外,如果这两个微服务是 RESTFul 的,那么 CommandManager 可以返回一个 202 Accepted 状态码,其中包含指向将来创建的资源的链接。然后客户端可以轮询该资源,直到服务器响应 200 OK 为止。 另一种解决方案是,CommandManager 返回一个 202 Accepted 状态码,并提供指向 command/status 终端点的链接。客户端会轮询该终端点,直到状态变为 command-processed(包括实际资源的 URL)或 command-failed(包括失败的描述性消息)为止。 这些解决方案可以通过使用 Server Sent Events 发送所有已处理命令的状态来增强。这样,客户端就可以在不轮询的情况下收到通知。 如果客户端不知道架构是异步的,一个解决方案是使用 API 网关阻塞客户端的请求,直到上游微服务处理完命令,然后响应完整的资源数据。

谢谢回复(你和@VoiceOfUnreason)。我喜欢使用命令状态来跟踪进度的解决方案。在这个解决方案中,我们需要审查UI以显示一些进度条或类似的东西。我不知道是否有一些框架可以自动化完成这个任务。 - Nico
我使用服务器发送事件来报告长时间运行的命令的进度。它非常顺利地工作。 - Constantin Galbenu

3
在这一点上,前端不再等待响应,因为这种流程是异步的。我们该如何完成这个流程以便将用户转发到电影信息网页?我们应该等待创建过程完成后再查询QueryManager。
简短回答:使协议显式。
更长的回答:在这里寻找灵感的好地方是HTTP。
前端向源服务器进行POST请求;结果源服务器将消息放入队列并发送回响应。
发送此响应的表示应描述请求的当前状态,并指向(或嵌入)可以向用户提供请求完成估计的状态监视器。
然后客户端可以轮询端点以了解已经取得的进展情况。
例如,端点可能是对数据存储的查询,查找命令管理器是否处理了原始命令的证据;或者它可能是一个正在观察总线的端点,根据是否看到了MovieCreated消息来改变其答案。

为了更好地理解,可以研究一下幂等请求处理;当命令管理器从其队列中取出消息时,它如何知道是否已经处理过该消息的副本?您的轮询端点应该能够使用相同的信息来让消费者知道消息已成功处理。


2
除了@Constantin Galbenu的答案之外,我想补充一点。我强烈建议您查看一种名为“BFF”(Backend-For-Frontend)的微服务模式。与其让一个庞大的API网关做所有工作,不如针对每个用例使用一个API。例如,在您的情况下,您可以使用一个名为“CreateMovieBFFHandler”的API来接收前端的POST请求,然后该处理程序将与系统中的其他事物(如消息队列、事件等)协调以跟踪提交请求的状态。UI可能会与这个BFF处理程序有一个协议,如果响应在X秒内没有返回,则前端将认为它失败了;如果该处理程序能够从消息队列或“MovieCreated”事件中成功获取到已处理的消息,则它可以发送200 OK回来,然后您可以重定向页面来调用写入侧并填充UI。
有用的链接:https://samnewman.io/patterns/architectural/bff/

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接