使用RabbitMQ的工作池和多租户队列

时间:2011-11-28 20:24:11

标签: rabbitmq amqp worker

我在一个基于多租户云的应用程序的Web应用程序上工作(许多客户端,每个客户端都有自己独立的“环境”,但都在共享硬件集上),我们正在为用户引入这种能力批量处理以供以后处理。批量工作的类型实际上并不重要,只有足够的数量才能在没有工作队列的情况下进行,这实际上并不实际。我们选择RabbitMQ作为底层队列框架。

因为我们是一个多租户应用程序,所以我们不一定希望客户端能够为另一个客户端造成冗长的队列处理时间,所以我们浮出水面的一个想法是在每个客户端上创建一个队列基础并在所有客户端队列中指向共享工作池。问题在于,就我所能想到的最好,工人直接绑定到特定的队列,而不是交换。在我们理想的世界中,我们的客户端队列仍将被处理,而没有一个客户端阻止另一个客户端队列,我们​​可以通过启动更多工作人员或关闭空闲工作人员来根据需要增长或缩小共享工作者池。将工作人员绑定到特定队列可以防止我们从实际意义上解决这个问题,因为我们经常有很多工作人员闲置在没有活动的队列上。

是否有相对直接的目标来实现这一目标?我是RabbitMQ的新手,并没有真正完成我们所追求的目标。我们也不想编写一个非常复杂的多线程消费者应用程序,这是我们可能负担不起的开发时间和测试时间的时间。我们的堆栈是以Windows / .Net / C#为基础的,如果它是绅士,但我认为这不应该对手头的问题产生重大影响。

4 个答案:

答案 0 :(得分:3)

您可以查看优先级队列实现(最初询问此问题时未实现):https://www.rabbitmq.com/priority.html

如果这对您不起作用,您可以尝试其他一些黑客来实现您想要的东西(适用于旧版本的RabbitMQ):

您可以将100个队列绑定到主题交换,并将路由键设置为用户ID%100的哈希值,即每个任务的密钥在1到100之间,同一个用户的任务将具有相同的密钥。每个队列都绑定一个介于1和100之间的唯一模式。现在,您有一组工作人员以随机队列号开头,然后在每个作业之后递增该队列号,再次%100以在队列100之后循环回队列1。

现在,您的工作人员队伍可以并行处理多达100个唯一身份用户,或者如果没有其他工作要做,所有工作人员都可以专注于单个用户。如果工作人员需要遍历每个作业之间的所有100个队列,那么在单个用户在单个队列中只有大量作业的情况下,您自然会在每个作业之间产生一些开销。较少数量的队列是解决此问题的一种方法。您还可以让每个工作者保持与每个队列的连接,并从每个队列中消耗最多一条未确认的消息。然后,只要未确认的消息超时设置得足够高,工作人员就可以更快地循环遍历内存中的待处理消息。

或者,您可以创建两个交换,每个交换都有一个绑定队列。所有工作都进入第一个交换和队列,一组工人消耗。如果工作单元花费的时间太长,工作人员可以取消它并将其推送到第二个队列。当第一个队列中没有任何内容时,工作程序仅处理第二个队列。您可能还需要一些具有相反队列优先级的工作人员,以确保在永久结束的短任务流到达时仍处理长时间运行的任务,以便最终始终处理用户批处理。这不会真正将您的工作人员分配到所有任务中,但它会阻止一个用户阻止您的工作人员为同一个用户或另一个用户执行短期运行任务。它还假设您可以取消作业并在以后重新运行它而不会出现任何问题。这也意味着将会从超时的任务中浪费资源,并且需要以低优先级重新运行。除非您可以提前识别快速和慢速任务

如果单个用户有100个慢速任务,那么100个队列的第一个建议也会出现问题,然后另一个用户发布一批任务。在完成一个缓慢的任务之前,不会查看这些任务。如果这是一个合法的问题,你可能会将两种解决方案结合起来。

答案 1 :(得分:1)

您可以让您的工作池都使用相同的唯一队列。然后,工作将分布在他们之间,您将能够增加/缩小您的池,以增加/减少您的工作处理能力。

答案 2 :(得分:1)

我不明白为什么你不使用RabbitMQ的虚拟主机并让你的应用登录RabbitMQ并在每个用户的单独连接上进行身份验证。

这并不意味着您不能让工人主管将工人分配给一个或另一个用户。但它确实意味着每个用户的所有消息都由完全独立的交换和队列处理。

答案 3 :(得分:0)

工作人员被分配了0个以上的队列,而不是交换。

将通过CELERYD_CONSUMER指示的类实现从每个工作队的队列中执行任务的逻辑,默认为celery.worker.consumer.Consumer

您可以创建自定义使用者类ro实现您喜欢的任何逻辑。困难的部分是决定你想要使用的“公平”算法的细节;但是一旦你决定了,你可以实现它创建一个自定义的消费者类并将其分配给适当的工人。