Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 一个RabbitMQ队列上有多个使用者_Java_Rabbitmq - Fatal编程技术网

Java 一个RabbitMQ队列上有多个使用者

Java 一个RabbitMQ队列上有多个使用者,java,rabbitmq,Java,Rabbitmq,我遵循RabbitMQ的以下指南:。我想用一个队列上的多个线程来模拟这个功能 如果在启动发送方之前启动接收方,它将按预期工作,如下所示: [*] Rcvr1 Waiting for messages... [*] Rcvr2 Waiting for messages... [x] Rcvr1 Received 'Hello 0' [x] Rcvr2 Received 'Hello 1' [x] Rcvr1 Received 'Hello 2' [x] Rcvr2 Received 'Hello

我遵循RabbitMQ的以下指南:。我想用一个队列上的多个线程来模拟这个功能

如果在启动发送方之前启动接收方,它将按预期工作,如下所示:

[*] Rcvr1 Waiting for messages...
[*] Rcvr2 Waiting for messages...
[x] Rcvr1 Received 'Hello 0'
[x] Rcvr2 Received 'Hello 1'
[x] Rcvr1 Received 'Hello 2'
[x] Rcvr2 Received 'Hello 3'
[x] Rcvr1 Received 'Hello 4'
[x] Rcvr2 Received 'Hello 5'
[x] Rcvr1 Received 'Hello 6'
[x] Rcvr2 Received 'Hello 7'
[x] Rcvr1 Received 'Hello 8'
...
但是,首先启动接收器只会导致一个线程接收消息(最后一个要启动的线程):

有趣的是,如果我启动发送方,然后启动接收方,如上所述,然后再次启动发送方(而接收方正在处理第一批)。发送的第一批消息是串行处理的,而第二批消息是并行处理的,或者至少是与其余线程并行处理的:

 [*] Rcvr1 Waiting for messages...
 [*] Rcvr2 Waiting for messages...
 [x] Rcvr1 Received '[Batch 1] Hello 0'
 [x] Rcvr1 Received '[Batch 1] Hello 1'
 [x] Rcvr1 Received '[Batch 1] Hello 2'
 [x] Rcvr1 Received '[Batch 1] Hello 3'
 [x] Rcvr1 Received '[Batch 1] Hello 4'
 [x] Rcvr1 Received '[Batch 1] Hello 5'
 [x] Rcvr1 Received '[Batch 1] Hello 6'
 [x] Rcvr1 Received '[Batch 1] Hello 7'
 [x] Rcvr1 Received '[Batch 1] Hello 8'
 [x] Rcvr2 Received '[Batch 2] Hello 1'
 [x] Rcvr1 Received '[Batch 1] Hello 9'
 [x] Rcvr2 Received '[Batch 2] Hello 3'
 [x] Rcvr1 Received '[Batch 1] Hello 10'
 [x] Rcvr2 Received '[Batch 2] Hello 5'
 [x] Rcvr1 Received '[Batch 1] Hello 11'
 [x] Rcvr2 Received '[Batch 2] Hello 7'
 [x] Rcvr1 Received '[Batch 1] Hello 12'
 [x] Rcvr2 Received '[Batch 2] Hello 9'
 [x] Rcvr1 Received '[Batch 1] Hello 13'
 [x] Rcvr2 Received '[Batch 2] Hello 11'
这在RabbitMQ中显然是可能的,我不确定我做错了什么。我的简单代码如下:

寄件人

public class Send {

    private final static String QUEUE_NAME = "hello";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {
            channel.queueDeclare(QUEUE_NAME, true, false, false, null);
            for(int x=0; x<100; x++) {
                String message = "Hello "+x;
                channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
                System.out.println(" [x] Sent '" + message + "'");
            }
        }
    }
}
我还将这个例子与他们描述的完全一致,并且看到了相同的结果


我假设我的设置有问题

根据RabbitMQ api:

虽然一个通道可以由多个线程使用,但必须确保一次只有一个线程执行一个命令。同时执行命令可能会引发意外的FrameError

首先,我认为应该为不同的线程使用不同的通道

最后,我认为第一个线程被终止是因为它是空闲的,所以只有第二个线程是活动的,并完成整个工作。在这种情况下,一个线程就足够了

请看一下适用于java 8的ThreadPoolExecutor api:

例如,您可以找到:

默认情况下,即使核心线程最初也会创建并仅在新任务到达时启动,但这可以使用方法prestartCoreThread()或prestartAllCoreThreads()动态覆盖。如果您使用非空队列构建池,则可能需要预启动线程

如果池当前有多个corePoolSize线程,如果多余线程的空闲时间超过keepAliveTime(请参阅getKeepAliveTime(TimeUnit)),则会终止这些线程


您应该使用prestartAllCoreThreads()或prestartCoreThreads()在空闲时启动核心线程,或者使用getKeepAliveTime(时间单位)在空闲时保持它们的活动状态。

看起来我缺少一个关键通道配置。这解决了我的问题:

基本频道(1)

这是RabbitMQ对此的看法

公平调度

您可能已经注意到,调度仍然无法正常工作 正如我们所愿。例如,在有两名工人的情况下,所有工人都是奇数 消息很重,即使消息很轻,一名工人也会被解雇 总是很忙,而另一个几乎不做任何工作。好, RabbitMQ对此一无所知,仍将发送 平均发送消息

这是因为RabbitMQ只是在 消息进入队列。它并没有考虑 消费者的未确认消息。它只是盲目地发送 将每个第n条消息发送给第n个使用者

为了克服这一点,我们可以使用Basickos方法 预取计数=1设置。这告诉RabbitMQ不要给出超过 一次给一个工人一条消息。或者,换句话说,不要发送 向工作者发送新消息,直到其处理并确认 上一个。相反,它将把它发送给下一个工作人员 还不忙


嗨,萨克森,请看修改后的代码。我尝试了新的频道,甚至新的连接和频道。我甚至尝试创建两个应用程序实例(单线程),结果完全相同。首先启动的接收者获取所有消息,而第二个接收者不获取任何消息。你是对的。我很抱歉!我看不太清楚代码。您使用的是哪个java客户端版本?我使用的是客户端版本5.5.1
public class Send {

    private final static String QUEUE_NAME = "hello";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {
            channel.queueDeclare(QUEUE_NAME, true, false, false, null);
            for(int x=0; x<100; x++) {
                String message = "Hello "+x;
                channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
                System.out.println(" [x] Sent '" + message + "'");
            }
        }
    }
}
package com.mawv.ingest.rabbitmq;

import com.rabbitmq.client.*;

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class Recv {

    private final static String QUEUE_NAME = "hello";

    public static void main(String[] argv) throws Exception {
        ThreadPoolExecutor rcvrPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");

        Runnable rcvr1 = () -> {
            try {
                Connection connection = factory.newConnection();
                Channel channel = connection.createChannel();
                channel.queueDeclare(QUEUE_NAME, true, false, false, null);

                System.out.println(" [*] Rcvr1 Waiting for messages...");
                DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                    Envelope envelope = delivery.getEnvelope();
                    String message = new String(delivery.getBody(), "UTF-8");
                    System.out.println(" [x] Rcvr1 Received '" + message + "'");
                    long deliveryTag = envelope.getDeliveryTag();
                    channel.basicAck(deliveryTag, true);
                    try {
                        Thread.sleep(1000);
                    } catch (Exception ex) { }

                };
                channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> {  });

            } catch(Exception ex){
                ex.printStackTrace();
            }
        };
        Runnable rcvr2 = () -> {
            try {
                Connection connection = factory.newConnection();
                Channel channel = connection.createChannel();
                channel.queueDeclare(QUEUE_NAME, true, false, false, null);

                System.out.println(" [*] Rcvr2 Waiting for messages...");
                DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                    Envelope envelope = delivery.getEnvelope();
                    String message = new String(delivery.getBody(), "UTF-8");
                    System.out.println(" [x] Rcvr2 Received '" + message + "'");
                    long deliveryTag = envelope.getDeliveryTag();
                    channel.basicAck(deliveryTag, true);
                    try {
                        Thread.sleep(1000);
                    } catch (Exception ex) {
                    }
                };
                channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> {
                });
            } catch(Exception ex){
                ex.printStackTrace();
            }
        };
        rcvrPool.execute(rcvr1);
        rcvrPool.execute(rcvr2);

    }
}