如何在Redis上实现消息队列?


29

为什么要对Redis进行排队?

我的印象是Redis可以很好地实现排队系统。到目前为止,我们一直在使用MySQL数据库进行轮询或RabbitMQ。有了RabbitMQ,我们遇到了很多问题-客户端库非常差且存在漏洞,我们不想花费太多的开发人员时间来修复它们,也不希望在服务器管理控制台上遇到一些问题,等等。至少,我们没有掌握几毫秒,也没有认真提高性能,因此,只要系统的体系结构能够智能地支持队列,我们​​就可能处于良好状态。

好的,这就是背景。本质上,我有一个非常经典,简单的队列模型-几个生产者在生产工作,几个消费者在消费工作,生产者和消费者都需要能够智能地进行扩展。事实证明,天真PUBSUB是行不通的,因为我不希望所有订阅者都消费工作,我只希望一个订阅者可以接收工作。乍一看,在我看来,这BRPOPLPUSH是一个明智的设计。

我们可以使用BRPOPLPUSH吗?

基本设计BRPOPLPUSH是您有一个工作队列和一个进度队列。当消费者收到工作时,它会自动将项目推送到进度队列中,而当工作完成时,它就是工作LREM了。如果客户死了,这可以防止工作混乱,并使监视工作变得很轻松-例如,我们可以判断是否存在导致消费者花费大量时间来执行任务的问题,此外还可以判断是否存在大量任务。

它确保

  • 作品交付给了一位消费者
  • 工作在进度队列中结束,因此如果消费者使用它,则不会出现黑洞

缺点

  • 对于我来说,我发现的最好的设计实际上没有使用实际上是很奇怪的,PUBSUB因为这似乎是大多数有关Redis排队的博客所关注的内容。所以我觉得我缺少明显的东西。我看到PUBSUB不使用两次任务就使用的唯一方法是简单地推送一个通知,通知工作已经到达,然后消费者可以无阻塞地进行通知RPOPLPUSH
  • 一次请求一个以上的工作项目是不可能的,这似乎是一个性能问题。对于我们的情况而言,这不是一个很大的选择,但显然,该操作不是针对高吞吐量或这种情况而设计的
  • 简而言之:我是否缺少任何愚蠢的东西?

还添加了node.js标记,因为这是我最常使用的语言。考虑到Node的单线程和非阻塞性质,它可能会在实现方面提供一些简化,但是此外,我正在使用node-redis库,并且解决方案也应该或可以对其优点和缺点敏感。

Answers:


5

如果要在Node.js中将Redis用于消息队列,并且不介意为此使用模块,则可以尝试RSMQ-用于节点的Redis简单消息队列。在提出此问题时尚不可用,但今天它是一个可行的选择。

如果您想按照问题中的说明自己实际实现队列,那么您可能想阅读RSMQ的源代码,因为只有20个屏幕的代码完全可以满足您的要求。

看到:


我会接受这一点,除非以后知道它确实有缺陷或破损之类的东西。
djechlin

22

到目前为止,我遇到了一些困难,我想在这里记录一下。

您如何处理重新连接逻辑?

在设计和实现消息队列时,这是一个难题,尤其是难题。当使用者处于脱机状态时,消息必须能够在某个地方排队,因此,简单的pub-sub不够强大,并且使用者需要在侦听状态下重新连接。 阻塞的流行音乐很难维护,因为它们是非等幂的监听状态。侦听应该是幂等的操作,但是在处理与阻塞弹出有关的断开连接时,您可以非常认真地思考断开连接是在操作成功之后还是在操作失败之前发生的。这不是无法克服的,但这是不可取的。

此外,聆听操作应尽可能简单。 理想情况下,它应具有以下属性:

  • 听是幂等的。
  • 使用者始终在侦听,并且在侦听逻辑代码之外处理限制逻辑。RabbitMQ通过让使用者限制它可以拥有的未确认消息的数量来封装它。
    特别是,我采用了一个糟糕的设计,在该设计中,重新进入阻塞弹出窗口取决于先前操作的成功,这是脆弱的,需要认真思考。

我现在偏爱Redis PUBSUB + RPOPLPUSH解决方案。这使工作通知与工作消耗分离开来,这使我们可以提出一个干净的聆听解决方案。PUBSUB仅负责工作通知。RPOPLPUSH的原子性质负责消费,并将工作委派给恰好一个消费者。最初,与阻塞流行音乐相比,该解决方案似乎不必要地复杂,但是现在我看到了这种复杂性完全没有必要。它正在解决一个难题。

但是,此解决方案并不是很简单:

  • 消费者还应检查重新连接的工作。
  • 消费者可能仍希望对新工作进行轮询,以确保冗余。如果轮询实际成功,则应该发出警告,因为这仅发生在PUBSUB上的消耗与RPOPLPUSH上的轮询之间。因此,许多民意测验成功表明订阅系统已损坏。

请注意,PUBSUB / RPOPLPUSH设计也存在缩放问题。 每个消费者都会收到每条消息的轻量级通知,这意味着这有不必要的瓶颈。我怀疑可以使用渠道对工作进行分片,但这可能是一项棘手的设计,无法顺利完成。


不确定我是否阻止了消费者。在我看来,如果没有要处理的工作,消费者应该阻塞直到有一些事情发生为止,尽管我认为,如果消费者还在做其他事情,这可能是不同的故事,但这不是应用程序中更多的问题而不是排队那么多?IE不会在更大的应用程序中阻塞线程,这是一个更为优雅的解决方案,该线程将能够在应用程序从队列中检索到作业时通知应用程序。也许仅仅是节点的使用造成了复杂性。
AaronM 2014年

9
自从去年八月以来你走了多远,我很好奇。您是否能够满意地解决问题?您是如何解决它们的?
AaronM 2014年

3
AAA:就像@AaronM一样,我很想听听你的进步。
bjornl 2015年

同意 进展如何?我喜欢从堆栈中删除RabbitMQ并使用Redis的想法。我的问题是如何使用RSMQ(节点lib)注册使用者。
ra9r

@raiglstorfer在那里已经工作了两年了:P随时研究和发表...
djechlin

0

因此,选择在Re​​dis上使用RabbitMQ的最大原因是故障场景和集群。

本文确实对它做了最好的解释,所以我只提供链接:

https://aphyr.com/posts/283-jepsen-redis

Redis前哨和最近的Redis集群无法处理许多非常基本的故障情况,这使其成为队列的错误选择。

RabbitMQ有其自身的一系列问题,但是可以说它在生产中非常可靠,并且是一个很好的消息队列。

这是兔子的帖子:

https://aphyr.com/posts/315-jepsen-rabbitmq

当您查看CAP定理(一致性,可用性和分区处理)时,您只能选择3个中的2个。我们利用消息负载将RMQ用于CP(一致性和分区处理),如果我们不可用,则为“世界末日。为了不丢失消息,我们对分区处理使用了ignore,以便不丢失消息。由于源管理UUID,因此可以处理重复项。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.