RabbitMQ的。 Java客户端。是否可以在收到的同一个帖子上确认消息?

时间:2016-06-09 10:59:00

标签: java multithreading rabbitmq

我想获取几条消息,处理它们并在此之后将它们全部搞定。所以基本上我收到一条消息,把它放在一些队列中并继续从兔子接收消息。不同的线程将使用收到的消息监视此队列,并在金额足够时处理它们。我能找到的关于ack的所有内容仅包含一条在同一线程上处理的消息的示例。像这样(来自官方文档):

channel.basicQos(1);

final Consumer consumer = new DefaultConsumer(channel) {
  @Override
  public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    String message = new String(body, "UTF-8");

    System.out.println(" [x] Received '" + message + "'");
    try {
      doWork(message);
    } finally {
      System.out.println(" [x] Done");
      channel.basicAck(envelope.getDeliveryTag(), false);
    }
  }
};

文档也说明了这一点:

  

不得在线程之间共享通道实例。应用   应该更喜欢每个线程使用一个Channel而不是共享它   跨多个线程的通道。虽然渠道上的一些操作是   安全地同时调用,有些不会并且会导致错误   电线上的帧交错。

所以我在这里很困惑。如果我正在寻找一些消息,同时频道正在接收来自兔子的另一条消息,那么当时它被认为是两个操作吗?在我看来,是的。

我试图在不同线程的同一频道上确认消息并且它似乎有效,但文档说我不应该在线程之间共享通道。所以我尝试在不同的线程上使用不同的通道进行确认,但是它失败了,因为此通道的传递标签是未知的。

是否可以在收到的同一个帖子上确认消息?

UPD 我想要的一段代码示例。它是scala,但我认为这很简单。

 case class AmqpMessage(envelope: Envelope, msgBody: String)

    val queue = new ArrayBlockingQueue[AmqpMessage](100)

    val consumeChannel = connection.createChannel()
    consumeChannel.queueDeclare(queueName, true, false, true, null)
    consumeChannel.basicConsume(queueName, false, new DefaultConsumer(consumeChannel) {
      override def handleDelivery(consumerTag: String,
                                  envelope: Envelope,
                                  properties: BasicProperties,
                                  body: Array[Byte]): Unit = {
        queue.put(new AmqpMessage(envelope, new String(body)))
      }
    })

    Future {
      // this is different thread
      val channel = connection.createChannel()
      while (true) {
        try {
          val amqpMessage = queue.take()
          channel.basicAck(amqpMessage.envelope.getDeliveryTag, false) // doesn't work
          consumeChannel.basicAck(amqpMessage.envelope.getDeliveryTag, false) // works, but seems like not thread safe
        } catch {
          case e: Exception => e.printStackTrace()
        }
      }
    }

3 个答案:

答案 0 :(得分:1)

虽然文档非常严格,但通道上的某些操作可以安全地同时调用。 只要使用 acking 是您在频道上执行的唯一操作,您就可以在不同的线程中确认消息。

请参阅此SO问题,该问题涉及同样的事情:

RabbitMQ and channels Java thread safety

答案 1 :(得分:0)

对我来说,你的解决方案是正确的。您没有跨线程共享频道。 您永远不会将您的频道对象传递给另一个线程,而是在接收消息的同一线程上使用它。

你不可能

' 发送一些消息,同时频道正在接收来自兔子的另一条消息'

如果您使用 handleDelivery 方法,则该代码会阻止该线程,并且无法接收其他消息。

正如您所知,除了用于接收消息的频道之外,您无法使用频道确认消息。

您必须确认使用相同的频道,并且必须在接收消息的同一线程上执行此操作。所以你可以将channel对象传递给其他方法,但是你必须小心不要将它传递给另一个线程。

我在我的项目中使用此解决方案它使用RabbitMQ listner和Spring Integration。对于每个AMQP消息,都会创建一个org.springframework.integration.Message。该消息将AMPQ消息正文作为有效负载,AMQP通道和传递标记作为我的org.springframework.integration.Message的标题。

如果您要确认多条消息,并且它们是在同一频道上发送的,则应使用

channel.basicAck(envelope.getDeliveryTag(), true);

对于多个渠道,高效算法

  1. 假设您有100条消息,使用10个频道发送
  2. 您需要为每个频道找到max deliveryTag。
  3. 调用 channel.basicAck(maxDeliveryTagForThatChannel,true);
  4. 这样,你需要10个basicAck(网络往返)而不是100个。

答案 2 :(得分:0)

正如文档所说,每个线程一个通道其余部分没有限制。

我只想在你的例子上说几句话。你在这里想做的是错的。从ArrayBlockingQueue获取消息后,无需确认消息,因为一旦将消息放在那里,它就会保留在那里。向RMQ确认它与其他ArrayBlockingQueue队列无关。