Java RabbitMQ:快速生产者和慢速消费者

Java RabbitMQ:快速生产者和慢速消费者,java,multithreading,rabbitmq,amqp,producer-consumer,Java,Multithreading,Rabbitmq,Amqp,Producer Consumer,我有一个应用程序,它使用RabbitMQ作为消息队列在两个组件之间发送/接收消息:发送方和接收方。发送者以非常快的方式发送消息。接收方接收消息,然后执行一些非常耗时的任务(主要是为非常大的数据量编写数据库)。由于接收者需要很长时间来完成任务,然后检索队列中的下一条消息,因此发送者将继续快速填充队列。所以我的问题是:这会导致消息队列溢出吗 消息使用者如下所示: public void onMessage() throws IOException, InterruptedException {

我有一个应用程序,它使用RabbitMQ作为消息队列在两个组件之间发送/接收消息:发送方和接收方。发送者以非常快的方式发送消息。接收方接收消息,然后执行一些非常耗时的任务(主要是为非常大的数据量编写数据库)。由于接收者需要很长时间来完成任务,然后检索队列中的下一条消息,因此发送者将继续快速填充队列。所以我的问题是:这会导致消息队列溢出吗

消息使用者如下所示:

public void onMessage() throws IOException, InterruptedException {
    channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
    String queueName = channel.queueDeclare("allDataCase", true, false, false, null).getQueue();
    channel.queueBind(queueName, EXCHANGE_NAME, "");

    QueueingConsumer consumer = new QueueingConsumer(channel);
    channel.basicConsume(queueName, true, consumer);

    while (true) {
        QueueingConsumer.Delivery delivery = consumer.nextDelivery();
        String message = new String(delivery.getBody());
        System.out.println(" [x] Received '" + message + "'");

        JSONObject json = new JSONObject(message);
        String caseID = json.getString("caseID");
        //following takes very long time            
        dao.saveToDB(caseID);
    }
}

消费者收到的每条消息都包含一个caseID。对于每个caseID,它会将大量数据保存到数据库中,这需要很长时间。目前,RabbitMQ只设置了一个使用者,因为生产者/使用者对caseID的发布/订阅使用相同的队列。那么,我如何加快使用者吞吐量,以便使用者能够赶上生产者并避免队列中的消息溢出呢?我应该在消费者部分使用多线程来加速消费率吗?或者我应该使用多个消费者同时消费传入的消息吗?或者,是否有任何异步方式可以让使用者在不等待消息完成的情况下异步使用消息?欢迎提出任何建议

“那么我如何加快消费者吞吐量,使消费者能够赶上生产者,避免队列中的消息溢出?”这是答案“使用多个消费者同时消费传入消息”,使用多线程并行运行这些消费者实现无共享原则,作为回答,我建议:两者都要

您可以利用拥有多个接收者的优势,以及将每个接收者设置为在单独的线程中执行任务,从而允许接收者接受队列中的下一条消息


当然,这种方法假设每个操作的结果(如果我理解正确的话,在db上的写入)不会以任何方式影响其他消息响应的后续操作的结果。

您有很多方法来提高性能

  • 您可以创建一个具有更多生产者的工作队列,这样您就可以创建一个简单的负载平衡系统。不要使用exchange-->queue,而只使用queue。读这篇文章

  • 当您收到一条消息时,您可以创建一个池线程来在数据库中插入数据,但在这种情况下,您必须管理失败

  • 但我认为主要的问题是数据库,而不是RabbitMQ。通过良好的调优、多线程和工作队列,您可以拥有一个可扩展且快速的解决方案

    让我知道

    “这会导致消息队列溢出吗?”

    对。RabbitMQ将进入“流控制”状态,以防止队列长度增加时内存消耗过多。它还将开始将消息持久化到磁盘,而不是保存在内存中

    “那么,我如何加快消费者吞吐量,使消费者 可以赶上生产者,避免消息溢出 队列“

    您有两种选择:

  • 增加更多的消费者。请记住,如果选择此选项,您的数据库现在将由多个并发进程操作。确保DB能够承受额外的压力
  • 增加消费频道的QOS值。这将从队列中提取更多消息,并将它们缓冲在使用者上。这将增加总体处理时间;如果缓冲了5条消息,则第5条消息需要消息1…5的处理时间才能完成
  • “我应该在使用者部分使用多线程来加速 消费率?”

    除非你有一个精心设计的解决方案。向应用程序添加并行性将在用户端增加大量开销。最终可能会耗尽线程池或限制内存使用

    当处理AMQP时,你确实需要考虑每个过程的业务需求,以便设计最优的解决方案。你收到的信息对时间有多敏感?它们是否需要尽快持久化到DB,或者对于您的用户来说,数据是否立即可用是否重要


    如果数据不需要立即持久化,您可以修改应用程序,以便使用者只需从队列中删除消息并将其保存到缓存的集合中,例如在Redis中。引入第二个进程,然后依次读取和处理缓存的消息。这将确保队列长度不会增长到足以导致流控制,同时防止数据库被写请求轰炸,写请求通常比读请求更昂贵。您的使用者现在只需从队列中删除消息,稍后由另一个进程处理。

    虽然添加更多使用者可能会加快速度,但真正的问题将保存到数据库中

    这里已经有很多关于添加使用者(线程和/或机器)和更改QoS的答案,所以我不再重复。相反,您应该认真考虑使用该模式将消息聚合成一组消息,然后批量将该组插入到数据库中。 每个消息的当前代码可能会打开一个连接,插入数据,然后关闭该连接(或返回池)。更糟糕的是,它甚至可能正在使用事务

    通过使用聚合器模式,您可以在刷新之前缓冲数据

    现在编写一个好的聚合器是很棘手的。您需要决定如何缓冲(即每个工人都有自己的缓冲区或像Redis这样的中央缓冲区)。我相信Spring integration有一个聚合器。

    从中,这里有两种方法:工作队列、发布/订阅。我是我们