我想获取几条消息,处理它们并在此之后将它们全部搞定。所以基本上我收到一条消息,把它放在一些队列中并继续从兔子接收消息。不同的线程将使用收到的消息监视此队列,并在金额足够时处理它们。我能找到的关于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()
}
}
}
答案 0 :(得分:1)
虽然文档非常严格,但通道上的某些操作可以安全地同时调用。 只要使用和 acking 是您在频道上执行的唯一操作,您就可以在不同的线程中确认消息。
请参阅此SO问题,该问题涉及同样的事情:
答案 1 :(得分:0)
对我来说,你的解决方案是正确的。您没有跨线程共享频道。 您永远不会将您的频道对象传递给另一个线程,而是在接收消息的同一线程上使用它。
你不可能
' 发送一些消息,同时频道正在接收来自兔子的另一条消息'
如果您使用 handleDelivery 方法,则该代码会阻止该线程,并且无法接收其他消息。
正如您所知,除了用于接收消息的频道之外,您无法使用频道确认消息。
您必须确认使用相同的频道,并且必须在接收消息的同一线程上执行此操作。所以你可以将channel对象传递给其他方法,但是你必须小心不要将它传递给另一个线程。
我在我的项目中使用此解决方案它使用RabbitMQ listner和Spring Integration。对于每个AMQP消息,都会创建一个org.springframework.integration.Message。该消息将AMPQ消息正文作为有效负载,AMQP通道和传递标记作为我的org.springframework.integration.Message的标题。
如果您要确认多条消息,并且它们是在同一频道上发送的,则应使用
channel.basicAck(envelope.getDeliveryTag(), true);
对于多个渠道,高效算法
这样,你需要10个basicAck(网络往返)而不是100个。
答案 2 :(得分:0)
正如文档所说,每个线程一个通道其余部分没有限制。
我只想在你的例子上说几句话。你在这里想做的是错的。从ArrayBlockingQueue
获取消息后,无需确认消息,因为一旦将消息放在那里,它就会保留在那里。向RMQ确认它与其他ArrayBlockingQueue
队列无关。