Pub-Sub 模式和消息代理,如何确保所有订阅者完成事件处理
Pub-Sub pattern and message broker, how to make sure all subscribers finished working on event
首先,我知道有像 NATS 这样的好(和轻量级)消息代理。如果这是一份工作,我肯定会选择经过验证的解决方案,这更多的是关于好奇心和理解的意愿。
假设我想构建一个像 CRM 这样的系统,我想将它基于微服务,以便它易于扩展并适应工作负载。由于微服务应该解耦。发布-订阅进来了。为了让 pub-sub 按预期工作(发布者和订阅者的解耦),我需要一个消息系统。假设我想用 node.js 来实现这一点(充分意识到有很多更快的方法可以完成这项工作)。
我的 "issue" 或可能只是认知上的失败是让我全神贯注于如何确保所有订阅者都收到来自他们订阅的主题的消息?
client/frontend 向代理发送事件请求。代理可能会验证消息并将其放在预期的队列中。有 2 个微服务订阅了这个队列。代理现在只发送队列中最旧的事件,并回调到两个微服务。
当其中一个微服务比另一个慢得多时,这不会导致问题吗?
我的意思是,只要我不想发回表明任务已由所有订阅者完成的确认消息,它就应该可以工作。客户端不知道事件请求涉及多少服务,因此无法对其进行跟踪。所以需要broker来做。
这是否意味着我需要将其包含在消息代理中?它跟踪给定事件的订阅服务计算状态?
经过更多研究并在床上醒着几个小时后,我得出的结论是,如果发布者想要按顺序接收 responses/acknowledgements,那么让多个订阅者订阅一个 subject/topic 应该被视为不好的做法跟踪已发送 request/message/event.
的状态
经过更多思考后,我得出结论,对同一主题的多个订阅服务很可能永远没有必要 - 至少在我的场景中,只要我正确设计服务。我唯一能想到的场景是在不触及已经部署的服务的情况下在稍后的时间点添加某些功能。这感觉像是对不合适的服务设计的修复。
然后我想无论如何我都可以管理它并想出了 3 种方法。
首先是标准结构
我想不需要进一步的解释。一些方法不要在意细节,这只是一个头脑风暴的版本,肯定不理想。显示模式就够了。
方法 1 - 聚合器收集响应
由于 Broker 跟踪每个订阅者,它总是知道(或可以轻松计算)预期的响应数量。因此,它可以将响应消息重定向到聚合器主题,该主题在 sent/published 需要响应或成功消息的消息时自动创建(想想一些客户数据的更新 - 你显然想知道消息已通过并成功处理)。
当然,聚合器始终可以介于两者之间,即使只有一个响应返回。这将减少要涵盖的案件数量。聚合器基本上是某种代理。尽管如此,它仍然增加了 Broker 的复杂性。
方法 2 - 代理发布确认消息
首先:不要介意右边的连接混乱。它作为草图对我有用,但远非整洁。
正在发布的每条消息都由 Broker 以确认消息进行答复。该消息被放置在消息个人主题堆栈中。由于 Broker 知道每个 Subject 有多少个 Subscribers,它可以发回 Publisher 应该期望多少响应。一般来说,确认消息也有助于通知发布者他们的 message/event/request 是否被接受(想想这里的身份验证和授权模式)。
只要发布者始终需要响应,这就会起作用。如果没有,消息可能会保留很长一段时间。超时可以解决这个问题。
方法 3 - 传输协议响应
这与方法 2 非常相似,不同之处在于传输协议用于通知发布者已发送请求的状态和预期的潜在响应数量。
由于大多数(如果不是全部)适用于这种地形的协议都提供了某种响应消息的方式,并且由于无论如何都应该首先使用这些来验证消息是否已成功发送,因此答案也可以包含有效负载不仅通知客户端成功传输,还通知客户端预期有多少响应。
结论
我想说聚合器方法的开销太大,它需要比仅使用传输协议或消息系统本身更多的额外代码。聚合器很有趣,因为客户端可以完全忽略服务,因此是解耦的。
消息系统的使用对于日志记录(潜在的调试)和 Sagas 的实现(事件链)也很有趣。
备注
我不会将这些方法中的任何一种作为最佳实践进行推广。我只想用我的研究结果回答我自己的问题。
正如您通过自己的思考发现的那样,使用 1 个队列很难实现 pub-sub/topic 模式,因为那样 1 个队列就必须跟踪发送给每个订阅者的消息。这对那 1 个队列负有很大的责任。
通常,pub-sub/topic 模式是使用多个队列实现的:
- 一个用于接收消息
- 每个订阅者一个(订阅者队列)。
订户队列充当特定地址的邮箱。如果您有 5 个订阅者,那么您将有 5 个订阅者队列。
代理将根据订阅者队列重新填充的配置速率将消息从传入队列分发到每个订阅者队列。
这允许每个队列只处理 1 个特定的订阅者,并且可以更容易地跟踪订阅者使用确认消费的内容。此外,每个订阅者都能够按照自己的节奏使用其订阅者队列中的消息。
首先,我知道有像 NATS 这样的好(和轻量级)消息代理。如果这是一份工作,我肯定会选择经过验证的解决方案,这更多的是关于好奇心和理解的意愿。
假设我想构建一个像 CRM 这样的系统,我想将它基于微服务,以便它易于扩展并适应工作负载。由于微服务应该解耦。发布-订阅进来了。为了让 pub-sub 按预期工作(发布者和订阅者的解耦),我需要一个消息系统。假设我想用 node.js 来实现这一点(充分意识到有很多更快的方法可以完成这项工作)。
我的 "issue" 或可能只是认知上的失败是让我全神贯注于如何确保所有订阅者都收到来自他们订阅的主题的消息?
client/frontend 向代理发送事件请求。代理可能会验证消息并将其放在预期的队列中。有 2 个微服务订阅了这个队列。代理现在只发送队列中最旧的事件,并回调到两个微服务。
当其中一个微服务比另一个慢得多时,这不会导致问题吗?
我的意思是,只要我不想发回表明任务已由所有订阅者完成的确认消息,它就应该可以工作。客户端不知道事件请求涉及多少服务,因此无法对其进行跟踪。所以需要broker来做。
这是否意味着我需要将其包含在消息代理中?它跟踪给定事件的订阅服务计算状态?
经过更多研究并在床上醒着几个小时后,我得出的结论是,如果发布者想要按顺序接收 responses/acknowledgements,那么让多个订阅者订阅一个 subject/topic 应该被视为不好的做法跟踪已发送 request/message/event.
的状态经过更多思考后,我得出结论,对同一主题的多个订阅服务很可能永远没有必要 - 至少在我的场景中,只要我正确设计服务。我唯一能想到的场景是在不触及已经部署的服务的情况下在稍后的时间点添加某些功能。这感觉像是对不合适的服务设计的修复。
然后我想无论如何我都可以管理它并想出了 3 种方法。
首先是标准结构
方法 1 - 聚合器收集响应
当然,聚合器始终可以介于两者之间,即使只有一个响应返回。这将减少要涵盖的案件数量。聚合器基本上是某种代理。尽管如此,它仍然增加了 Broker 的复杂性。
方法 2 - 代理发布确认消息
正在发布的每条消息都由 Broker 以确认消息进行答复。该消息被放置在消息个人主题堆栈中。由于 Broker 知道每个 Subject 有多少个 Subscribers,它可以发回 Publisher 应该期望多少响应。一般来说,确认消息也有助于通知发布者他们的 message/event/request 是否被接受(想想这里的身份验证和授权模式)。
只要发布者始终需要响应,这就会起作用。如果没有,消息可能会保留很长一段时间。超时可以解决这个问题。
方法 3 - 传输协议响应
由于大多数(如果不是全部)适用于这种地形的协议都提供了某种响应消息的方式,并且由于无论如何都应该首先使用这些来验证消息是否已成功发送,因此答案也可以包含有效负载不仅通知客户端成功传输,还通知客户端预期有多少响应。
结论
我想说聚合器方法的开销太大,它需要比仅使用传输协议或消息系统本身更多的额外代码。聚合器很有趣,因为客户端可以完全忽略服务,因此是解耦的。
消息系统的使用对于日志记录(潜在的调试)和 Sagas 的实现(事件链)也很有趣。
备注
我不会将这些方法中的任何一种作为最佳实践进行推广。我只想用我的研究结果回答我自己的问题。
正如您通过自己的思考发现的那样,使用 1 个队列很难实现 pub-sub/topic 模式,因为那样 1 个队列就必须跟踪发送给每个订阅者的消息。这对那 1 个队列负有很大的责任。
通常,pub-sub/topic 模式是使用多个队列实现的:
- 一个用于接收消息
- 每个订阅者一个(订阅者队列)。
订户队列充当特定地址的邮箱。如果您有 5 个订阅者,那么您将有 5 个订阅者队列。
代理将根据订阅者队列重新填充的配置速率将消息从传入队列分发到每个订阅者队列。
这允许每个队列只处理 1 个特定的订阅者,并且可以更容易地跟踪订阅者使用确认消费的内容。此外,每个订阅者都能够按照自己的节奏使用其订阅者队列中的消息。