我想在RabbitMQ和Spring AMQP中使用发布者确认,如果侦听器在处理消息期间抛出异常,则消息确认回调将获得NACK。
关注this blog post,我说的是用红色标记的用例:
主要问题是:
如何配置ConnectionFactory,RabbitTemplate和ListenerContainer以启用手动NACK?
我在侦听器中需要做什么才能在发生异常的情况下NACK消息并使用success = false
调用确认回调?
这是我的豆子:
@Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
connectionFactory.setPublisherConfirms(true);
return connectionFactory;
}
@Bean
public ConfirmCallback confirmCallback() {
return new ConfirmCallbackTestImplementation();
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory, ConfirmCallback confirmCallback) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setConfirmCallback(confirmCallback);
rabbitTemplate.setExchange(DIRECT_EXCHANGE);
return rabbitTemplate;
}
@Bean
public FaultyMessageListener faultyListener(RabbitAdmin rabbitAdmin, DirectExchange exchange, ConnectionFactory connectionFactory) {
Queue queue = queue(rabbitAdmin, exchange, "faultyListener");
FaultyMessageListener listener = new FaultyMessageListener();
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setMessageListener(listener);
container.setQueues(queue);
container.setDefaultRequeueRejected(false);
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
container.start();
return listener;
}
private Queue queue(RabbitAdmin rabbitAdmin, DirectExchange exchange, String routingKey) {
Queue queue = new Queue(routingKey, true, false, true);
rabbitAdmin.declareQueue(queue);
rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(exchange).with(routingKey));
return queue;
}
这是我的Listener实现:
public class FaultyMessageListener implements ChannelAwareMessageListener {
private final List<Message> receivedMessages = new ArrayList<>();
private final CountDownLatch latch = new CountDownLatch(1);
@Override
public void onMessage(Message message, Channel channel) throws Exception {
receivedMessages.add(message);
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
latch.countDown();
throw new AmqpException("Message could not be processed");
}
}
这是我的确认回电:
public static class ConfirmCallbackTestImplementation implements ConfirmCallback {
private volatile Map<String, Boolean> confirmations = new HashMap<>();
private volatile HashMap<String, CountDownLatch> expectationLatches = new HashMap<>();
@Override
public void confirm(CorrelationData correlationData, boolean success, String s) {
confirmations.put(correlationData.getId(), success);
expectationLatches.get(correlationData.getId()).countDown();
}
public CountDownLatch expect(String correlationId) {
CountDownLatch latch = new CountDownLatch(1);
this.expectationLatches.put(correlationId, latch);
return latch;
}
}
然后我使用以下测试来验证所需的行为:
@Autowired
private RabbitTemplate template;
@Autowired
private FaultyMessageListener faultyListener;
@Autowired
private ConfirmCallbackTestImplementation testConfirmCallback;
@Test
public void sendMessageToFaultyMessageListenerResultsInNack() throws InterruptedException {
String correlationId = "corr-data-test-2";
CountDownLatch confirmationLatch = testConfirmCallback.expect(correlationId);
template.convertAndSend("ConnectionsTests.PublisherConfirm", "faultyListener", "faulty message", new CorrelationData(correlationId));
assertTrue(faultyListener.latch.await(1, TimeUnit.SECONDS));
confirmationLatch.await(1, TimeUnit.SECONDS);
assertThat(faultyListener.receivedMessages.size(), is(1));
assertThat(testConfirmCallback.confirmations.get(correlationId), is(false));
}
测试结果如下:
java.lang.AssertionError:
Expected: is <false>
but: was <true>
为最后一个断言。对我而言,这类似于确认回调总是使用success = true
而不是success = false
来调用我对听众channel.basicNack(...)
的期望。
答案 0 :(得分:3)
它没有那样的工作;发布者方面的ack / nack纯粹是经纪人是否已接受该消息。实际上,很少返回nack,因为它意味着代理本身存在问题 - 请参阅the rabbit documentation。
只有在负责队列的Erlang进程中发生内部错误时,才会发送basic.nack。
同样,消费者方面的ack / nack纯粹是关于消费者是否已接受对消息的责任,而nack允许消息被重新排队,丢弃或路由到死信队列。
消息发布后,消费者无法与发布者进行通信。如果您需要此类通信,则需要设置回复队列。
如果您希望发布商和消费者之间存在紧密联系,则可以使用Spring Remoting (RPC) Over RabbitMQ。如果使用者抛出异常,它将被传播回发布者 - 但是,该机制仅支持Java Serializable
对象。
虽然文档引用了XML,但您可以将代理和服务调用程序连接起来@Bean