可靠的Webhook调度系统
Reliable Webhook dispatching system
我很难为 webhook 调度系统找出可靠且可扩展的解决方案。
当前系统使用 RabbitMQ
和一个 webhook 队列(我们称之为 events
),它们被消耗和调度。这个系统用了一段时间,但现在有几个问题:
- 如果一个系统用户产生过多事件,会占用队列导致其他用户长时间收不到webhook
- 如果我将所有事件分成多个队列(通过 URL 哈希),它会减少第一个问题的可能性,但当非常繁忙的用户访问同一个队列时,它仍然会不时发生
- 如果我尝试将每个 URL 放入其自己的队列中,挑战是动态地 create/assign 消费者到这些队列。就
RabbitMQ
文档而言,API 在过滤非空队列或未分配消费者的队列方面非常有限。
- 就
Kafka
而言,据我了解,在单个分区的范围内,情况是相同的。
所以,问题是 - 是否有更好的 way/system 用于此目的?也许我缺少一个非常简单的解决方案,可以让一个用户不干扰另一个用户?
提前致谢!
您可以尝试几个 rabbitmq 功能来缓解您的问题(无需完全删除它):
使用 public random exchange 将事件拆分到多个队列中。它将减轻事件的大量峰值并将工作分派给多个消费者。
为您的队列设置一些 TTL policies。这样,如果处理速度不够快,Rabbitmq 可能会将事件重新发布到另一组队列(例如,通过另一个私有随机交换)。
您可能有多个事件“周期”,配置不同(即周期数和每个周期的 TTL 值)。您的第一个周期会尽可能地处理新事件,在随机交换下通过多个队列减少峰值。如果它不能足够快地处理事件,事件将移动到另一个具有专用队列和消费者的周期。
这样,您可以确保新事件有更好的变化,以便快速处理,因为它们总是在第一个周期发布(而不是在另一个用户的一堆旧事件之后)。
如果您需要订单,很遗憾,您需要依赖用户输入。
但是在 Kafka 的世界里,有几件事要在这里提一下;
- 您可以使用
Transactions
实现 exactly-once
交付,这允许您构建与常规 AMQP 类似的系统。
- Kafka 支持按键分区。这允许您保持相同键的处理顺序(在您的情况下为 userId)。
- 可以通过调整所有生产者、服务器和消费者端(批量大小、飞行请求等。有关更多参数,请参阅 Kafka documentation)来提高吞吐量。
- Kafka 支持消息压缩,它可以减少网络流量并提高吞吐量(对于像 LZ4 这样的快速压缩算法,只是消耗更多 CPU 的能力)。
分区在你的场景中是最重要的。您可以增加分区以同时处理更多消息。您的消费者可以与同一消费者组中的分区一样多。即使您在达到分区数后进行扩展,您的新消费者也将无法读取并且他们将保持未分配状态。
与常规的 AMQP 服务不同,Kafka 在您阅读消息后不会删除消息,只是标记 consumer-gorup-id 的偏移量。这允许您同时做几件事。就像在单独的进程中计算实时用户数一样。
所以,我不确定这是否是解决此问题的正确方法,但这是我想出的方法。
先决条件:具有重复数据删除插件的 RabbitMQ
所以我的解决方案包括:
g:events
队列 - 我们称它为 parent
队列。该队列将包含需要处理的所有 child
个队列的名称。可能它可以用其他一些机制代替(比如 Redis sorted Set 之类的),但是你必须自己实现 ack 逻辑。
g:events:<url>
- 有 child
个队列。每个队列仅包含需要发送到 url
. 的事件
当post将 webhook 负载发送到 RabbitMQ 时,您 post 将实际数据发送到 child
队列,然后另外 post child
队列到 parent
队列。重复数据删除插件不允许相同的 child
队列被 post 编辑两次,这意味着只有一个消费者可以接收该 child
队列进行处理。
你们所有的消费者都在消费parent
队列,收到一条消息后,他们开始消费消息中指定的child
队列。在 child
队列为空后,您确认 parent
消息并继续。
此方法可以非常精细地控制允许处理哪些 child
队列。如果某些 child
队列花费了太多时间,只需 ack
parent
消息并将相同的数据重新发布到 parent
队列的末尾。
我知道这可能不是最有效的方法(不断 posting 到 parent
队列也有一些开销),但它就是这样。
我很难为 webhook 调度系统找出可靠且可扩展的解决方案。
当前系统使用 RabbitMQ
和一个 webhook 队列(我们称之为 events
),它们被消耗和调度。这个系统用了一段时间,但现在有几个问题:
- 如果一个系统用户产生过多事件,会占用队列导致其他用户长时间收不到webhook
- 如果我将所有事件分成多个队列(通过 URL 哈希),它会减少第一个问题的可能性,但当非常繁忙的用户访问同一个队列时,它仍然会不时发生
- 如果我尝试将每个 URL 放入其自己的队列中,挑战是动态地 create/assign 消费者到这些队列。就
RabbitMQ
文档而言,API 在过滤非空队列或未分配消费者的队列方面非常有限。 - 就
Kafka
而言,据我了解,在单个分区的范围内,情况是相同的。
所以,问题是 - 是否有更好的 way/system 用于此目的?也许我缺少一个非常简单的解决方案,可以让一个用户不干扰另一个用户?
提前致谢!
您可以尝试几个 rabbitmq 功能来缓解您的问题(无需完全删除它):
使用 public random exchange 将事件拆分到多个队列中。它将减轻事件的大量峰值并将工作分派给多个消费者。
为您的队列设置一些 TTL policies。这样,如果处理速度不够快,Rabbitmq 可能会将事件重新发布到另一组队列(例如,通过另一个私有随机交换)。
您可能有多个事件“周期”,配置不同(即周期数和每个周期的 TTL 值)。您的第一个周期会尽可能地处理新事件,在随机交换下通过多个队列减少峰值。如果它不能足够快地处理事件,事件将移动到另一个具有专用队列和消费者的周期。
这样,您可以确保新事件有更好的变化,以便快速处理,因为它们总是在第一个周期发布(而不是在另一个用户的一堆旧事件之后)。
如果您需要订单,很遗憾,您需要依赖用户输入。
但是在 Kafka 的世界里,有几件事要在这里提一下;
- 您可以使用
Transactions
实现exactly-once
交付,这允许您构建与常规 AMQP 类似的系统。 - Kafka 支持按键分区。这允许您保持相同键的处理顺序(在您的情况下为 userId)。
- 可以通过调整所有生产者、服务器和消费者端(批量大小、飞行请求等。有关更多参数,请参阅 Kafka documentation)来提高吞吐量。
- Kafka 支持消息压缩,它可以减少网络流量并提高吞吐量(对于像 LZ4 这样的快速压缩算法,只是消耗更多 CPU 的能力)。
分区在你的场景中是最重要的。您可以增加分区以同时处理更多消息。您的消费者可以与同一消费者组中的分区一样多。即使您在达到分区数后进行扩展,您的新消费者也将无法读取并且他们将保持未分配状态。
与常规的 AMQP 服务不同,Kafka 在您阅读消息后不会删除消息,只是标记 consumer-gorup-id 的偏移量。这允许您同时做几件事。就像在单独的进程中计算实时用户数一样。
所以,我不确定这是否是解决此问题的正确方法,但这是我想出的方法。
先决条件:具有重复数据删除插件的 RabbitMQ
所以我的解决方案包括:
g:events
队列 - 我们称它为parent
队列。该队列将包含需要处理的所有child
个队列的名称。可能它可以用其他一些机制代替(比如 Redis sorted Set 之类的),但是你必须自己实现 ack 逻辑。g:events:<url>
- 有child
个队列。每个队列仅包含需要发送到url
. 的事件
当post将 webhook 负载发送到 RabbitMQ 时,您 post 将实际数据发送到 child
队列,然后另外 post child
队列到 parent
队列。重复数据删除插件不允许相同的 child
队列被 post 编辑两次,这意味着只有一个消费者可以接收该 child
队列进行处理。
你们所有的消费者都在消费parent
队列,收到一条消息后,他们开始消费消息中指定的child
队列。在 child
队列为空后,您确认 parent
消息并继续。
此方法可以非常精细地控制允许处理哪些 child
队列。如果某些 child
队列花费了太多时间,只需 ack
parent
消息并将相同的数据重新发布到 parent
队列的末尾。
我知道这可能不是最有效的方法(不断 posting 到 parent
队列也有一些开销),但它就是这样。