@Override public void sendMessage(final Message message, final URI sendTo) { if (!IpUtil.isAmqpUri(sendTo)) { return; } final String exchange = sendTo.getPath().substring(1); final String correlationId = UUID.randomUUID().toString(); if (isCorrelationIdEmpty(message)) { message.getMessageProperties().setCorrelationId(correlationId); } if (LOGGER.isTraceEnabled()) { LOGGER.trace("Sending message {} to exchange {} with correlationId {}", message, exchange, correlationId); } else { LOGGER.debug("Sending message to exchange {} with correlationId {}", exchange, correlationId); } getRabbitTemplate().send(exchange, null, message, new CorrelationData(correlationId)); }
public void sendSimpleMessage(Map<String, Object> headers, Object message, String messageId, String exchangeName, String key) { // 自定义消息头 MessageHeaders messageHeaders = new MessageHeaders(headers); // 创建消息 Message<Object> msg = MessageBuilder.createMessage(message, messageHeaders); /* 确认的回调 确认消息是否到达 Broker 服务器 其实就是是否到达交换器 如果发送时候指定的交换器不存在 ack就是false 代表消息不可达 */ rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> { log.info("correlationData:{} , ack:{}", correlationData.getId(), ack); if (!ack) { System.out.println("进行对应的消息补偿机制"); } }); /* 消息失败的回调 * 例如消息已经到达交换器上,但路由键匹配任何绑定到该交换器的队列,会触发这个回调,此时 replyText: NO_ROUTE */ rabbitTemplate.setReturnCallback((message1, replyCode, replyText, exchange, routingKey) -> { log.info("message:{}; replyCode: {}; replyText: {} ; exchange:{} ; routingKey:{}", message1, replyCode, replyText, exchange, routingKey); }); // 在实际中ID 应该是全局唯一 能够唯一标识消息 消息不可达的时候触发ConfirmCallback回调方法时可以获取该值,进行对应的错误处理 CorrelationData correlationData = new CorrelationData(messageId); rabbitTemplate.convertAndSend(exchangeName, key, msg, correlationData); } }
@Test public void testConfirmReceivedAfterPublisherCallbackChannelScheduleClose() throws Exception { final CountDownLatch latch = new CountDownLatch(40); templateWithConfirmsEnabled.setConfirmCallback((correlationData, ack, cause) -> latch.countDown()); ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 20; i++) { executorService.execute(() -> { templateWithConfirmsEnabled.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); templateWithConfirmsEnabled.convertAndSend("BAD_ROUTE", (Object) "bad", new CorrelationData("cba")); }); } assertTrue(latch.await(10, TimeUnit.SECONDS)); assertNull(templateWithConfirmsEnabled.getUnconfirmed(-1)); }
@Test public void testPublisherReturns() throws Exception { final CountDownLatch latch = new CountDownLatch(1); final List<Message> returns = new ArrayList<Message>(); templateWithReturnsEnabled.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> { returns.add(message); latch.countDown(); }); templateWithReturnsEnabled.setMandatory(true); templateWithReturnsEnabled.convertAndSend(ROUTE + "junk", (Object) "message", new CorrelationData("abc")); assertTrue(latch.await(1000, TimeUnit.MILLISECONDS)); assertEquals(1, returns.size()); Message message = returns.get(0); assertEquals("message", new String(message.getBody(), "utf-8")); }
@Test public void testPublisherReturnsWithMandatoryExpression() throws Exception { final CountDownLatch latch = new CountDownLatch(1); final List<Message> returns = new ArrayList<Message>(); templateWithReturnsEnabled.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> { returns.add(message); latch.countDown(); }); Expression mandatoryExpression = new SpelExpressionParser().parseExpression("'message'.bytes == body"); templateWithReturnsEnabled.setMandatoryExpression(mandatoryExpression); templateWithReturnsEnabled.convertAndSend(ROUTE + "junk", (Object) "message", new CorrelationData("abc")); templateWithReturnsEnabled.convertAndSend(ROUTE + "junk", (Object) "foo", new CorrelationData("abc")); assertTrue(latch.await(1000, TimeUnit.MILLISECONDS)); assertEquals(1, returns.size()); Message message = returns.get(0); assertEquals("message", new String(message.getBody(), "utf-8")); }
@Test public void testNackForBadExchange() throws Exception { final AtomicBoolean nack = new AtomicBoolean(true); final AtomicReference<CorrelationData> correlation = new AtomicReference<CorrelationData>(); final AtomicReference<String> reason = new AtomicReference<String>(); final CountDownLatch latch = new CountDownLatch(2); this.templateWithConfirmsEnabled.setConfirmCallback((correlationData, ack, cause) -> { nack.set(ack); correlation.set(correlationData); reason.set(cause); latch.countDown(); }); Log logger = spy(TestUtils.getPropertyValue(connectionFactoryWithConfirmsEnabled, "logger", Log.class)); final AtomicReference<String> log = new AtomicReference<String>(); doAnswer(invocation -> { log.set((String) invocation.getArguments()[0]); invocation.callRealMethod(); latch.countDown(); return null; }).when(logger).error(any()); new DirectFieldAccessor(connectionFactoryWithConfirmsEnabled).setPropertyValue("logger", logger); CorrelationData correlationData = new CorrelationData("bar"); String exchange = UUID.randomUUID().toString(); this.templateWithConfirmsEnabled.convertAndSend(exchange, "key", "foo", correlationData); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertFalse(nack.get()); assertEquals(correlationData.toString(), correlation.get().toString()); assertThat(reason.get(), containsString("NOT_FOUND - no exchange '" + exchange)); assertThat(log.get(), containsString("NOT_FOUND - no exchange '" + exchange)); }
@Test public void testPublisherCallbackChannelImplCloseWithPending() throws Exception { Listener listener = mock(Listener.class); final CountDownLatch latch = new CountDownLatch(2); doAnswer(invocation -> { boolean ack = invocation.getArgument(1); if (!ack) { latch.countDown(); } return null; }).when(listener).handleConfirm(any(PendingConfirm.class), anyBoolean()); when(listener.getUUID()).thenReturn(UUID.randomUUID().toString()); when(listener.isConfirmListener()).thenReturn(true); Channel channelMock = mock(Channel.class); PublisherCallbackChannelImpl channel = new PublisherCallbackChannelImpl(channelMock, this.executorService); channel.addListener(listener); for (int i = 0; i < 2; i++) { long seq = i + 1000; channel.addPendingConfirm(listener, seq, new PendingConfirm(new CorrelationData(Long.toHexString(seq)), System.currentTimeMillis())); } channel.close(); assertTrue(latch.await(10, TimeUnit.SECONDS)); int n = 0; while (n++ < 100 && TestUtils.getPropertyValue(channel, "pendingConfirms", Map.class).size() > 0) { Thread.sleep(100); } assertEquals(0, TestUtils.getPropertyValue(channel, "pendingConfirms", Map.class).size()); }
@Test public void testPublisherConfirmReceivedConcurrentThreads() throws Exception { final CountDownLatch latch = new CountDownLatch(2); templateWithConfirmsEnabled.setConfirmCallback((correlationData, ack, cause) -> latch.countDown()); // Hold up the first thread so we get two channels final CountDownLatch threadLatch = new CountDownLatch(1); //Thread 1 Executors.newSingleThreadExecutor().execute(() -> templateWithConfirmsEnabled.execute(channel -> { try { threadLatch.await(10, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } templateWithConfirmsEnabled.doSend(channel, "", ROUTE, new SimpleMessageConverter().toMessage("message", new MessageProperties()), false, new CorrelationData("def")); return null; })); // Thread 2 templateWithConfirmsEnabled.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); threadLatch.countDown(); assertTrue(latch.await(5000, TimeUnit.MILLISECONDS)); assertNull(templateWithConfirmsEnabled.getUnconfirmed(-1)); }
@Test public void testPublisherConfirmReceivedTwoTemplates() throws Exception { final CountDownLatch latch1 = new CountDownLatch(1); final CountDownLatch latch2 = new CountDownLatch(1); templateWithConfirmsEnabled.setConfirmCallback((correlationData, ack, cause) -> latch1.countDown()); templateWithConfirmsEnabled.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); RabbitTemplate secondTemplate = new RabbitTemplate(connectionFactoryWithConfirmsEnabled); secondTemplate.setConfirmCallback((correlationData, ack, cause) -> latch2.countDown()); secondTemplate.convertAndSend(ROUTE, (Object) "message", new CorrelationData("def")); assertTrue(latch1.await(10, TimeUnit.SECONDS)); assertTrue(latch2.await(10, TimeUnit.SECONDS)); assertNull(templateWithConfirmsEnabled.getUnconfirmed(-1)); assertNull(secondTemplate.getUnconfirmed(-1)); }
for (int i = 0; i < 1000; i++) { try { t.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc"));
@Test public void testPublisherConfirmMultiple() throws Exception { ConnectionFactory mockConnectionFactory = mock(ConnectionFactory.class); Connection mockConnection = mock(Connection.class); Channel mockChannel = mock(Channel.class); when(mockChannel.isOpen()).thenReturn(true); when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection); when(mockConnection.isOpen()).thenReturn(true); PublisherCallbackChannelImpl callbackChannel = new PublisherCallbackChannelImpl(mockChannel, this.executorService); when(mockConnection.createChannel()).thenReturn(callbackChannel); final AtomicLong count = new AtomicLong(); doAnswer(invocation -> count.incrementAndGet()).when(mockChannel).getNextPublishSeqNo(); CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory); ccf.setExecutor(mock(ExecutorService.class)); ccf.setPublisherConfirms(true); final RabbitTemplate template = new RabbitTemplate(ccf); final CountDownLatch latch = new CountDownLatch(2); template.setConfirmCallback((correlationData, ack, cause) -> { if (ack) { latch.countDown(); } }); template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("def")); callbackChannel.handleAck(2, true); assertTrue(latch.await(1000, TimeUnit.MILLISECONDS)); Collection<CorrelationData> unconfirmed = template.getUnconfirmed(-1); assertNull(unconfirmed); }
@Override public RabbitMessageFuture sendAndReceive(String exchange, String routingKey, Message message) { String correlationId = getOrSetCorrelationIdAndSetReplyTo(message); RabbitMessageFuture future = new RabbitMessageFuture(correlationId, message); CorrelationData correlationData = null; if (this.enableConfirms) { correlationData = new CorrelationData(correlationId); future.setConfirm(new SettableListenableFuture<>()); } this.pending.put(correlationId, future); if (this.container != null) { this.template.send(exchange, routingKey, message, correlationData); } else { ChannelHolder channelHolder = this.directReplyToContainer.getChannelHolder(); future.setChannelHolder(channelHolder); sendDirect(channelHolder.getChannel(), exchange, routingKey, message, correlationData); } future.startTimer(); return future; }
@Test public void testPublisherConfirmNotReceived() throws Exception { ConnectionFactory mockConnectionFactory = mock(ConnectionFactory.class); Connection mockConnection = mock(Connection.class); Channel mockChannel = mock(Channel.class); when(mockChannel.isOpen()).thenReturn(true); when(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).thenReturn(mockConnection); when(mockConnection.isOpen()).thenReturn(true); doReturn(new PublisherCallbackChannelImpl(mockChannel, this.executorService)) .when(mockConnection) .createChannel(); CachingConnectionFactory ccf = new CachingConnectionFactory(mockConnectionFactory); ccf.setExecutor(mock(ExecutorService.class)); ccf.setPublisherConfirms(true); final RabbitTemplate template = new RabbitTemplate(ccf); final AtomicBoolean confirmed = new AtomicBoolean(); template.setConfirmCallback((correlationData, ack, cause) -> confirmed.set(true)); template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); Thread.sleep(5); assertEquals(1, template.getUnconfirmedCount()); Collection<CorrelationData> unconfirmed = template.getUnconfirmed(-1); assertEquals(0, template.getUnconfirmedCount()); assertEquals(1, unconfirmed.size()); assertEquals("abc", unconfirmed.iterator().next().getId()); assertFalse(confirmed.get()); }
@Override public RabbitMessageFuture sendAndReceive(String exchange, String routingKey, Message message) { String correlationId = getOrSetCorrelationIdAndSetReplyTo(message); RabbitMessageFuture future = new RabbitMessageFuture(correlationId, message); CorrelationData correlationData = null; if (this.enableConfirms) { correlationData = new CorrelationData(correlationId); future.setConfirm(new SettableListenableFuture<>()); } this.pending.put(correlationId, future); if (this.container != null) { this.template.send(exchange, routingKey, message, correlationData); } else { ChannelHolder channelHolder = this.directReplyToContainer.getChannelHolder(); future.setChannelHolder(channelHolder); sendDirect(channelHolder.getChannel(), exchange, routingKey, message, correlationData); } future.startTimer(); return future; }
template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); Thread.sleep(100); template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("def")); assertEquals(2, template.getUnconfirmedCount()); Collection<CorrelationData> unconfirmed = template.getUnconfirmed(50);
latch.countDown(); }); this.templateWithConfirmsEnabled.setCorrelationDataPostProcessor((m, c) -> new CorrelationData("abc")); final CountDownLatch mppLatch = new CountDownLatch(10000); MessagePostProcessor mpp = new MessagePostProcessor() {
template1.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); template2.convertAndSend(ROUTE, (Object) "message", new CorrelationData("def")); template2.convertAndSend(ROUTE, (Object) "message", new CorrelationData("ghi")); callbackChannel.handleAck(3, true); assertTrue(latch1.await(1000, TimeUnit.MILLISECONDS));
@Test public void testPublisherConfirmWithSendAndReceive() throws Exception { final CountDownLatch latch = new CountDownLatch(1); final AtomicReference<CorrelationData> confirmCD = new AtomicReference<CorrelationData>(); templateWithConfirmsEnabled.setConfirmCallback((correlationData, ack, cause) -> { confirmCD.set(correlationData); latch.countDown(); }); SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(this.connectionFactoryWithConfirmsEnabled); container.setQueueNames(ROUTE); container.setMessageListener( new MessageListenerAdapter((ReplyingMessageListener<String, String>) String::toUpperCase)); container.start(); CorrelationData correlationData = new CorrelationData("abc"); String result = (String) this.templateWithConfirmsEnabled.convertSendAndReceive(ROUTE, (Object) "message", correlationData); container.stop(); assertEquals("MESSAGE", result); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertEquals(correlationData, confirmCD.get()); }
new SimpleMessageConverter().toMessage("message", new MessageProperties()), false, new CorrelationData("def")); threadSentLatch.countDown(); return null; template.convertAndSend(ROUTE, (Object) "message", new CorrelationData("abc")); // channel y threadLatch.countDown(); assertTrue(threadSentLatch.await(5, TimeUnit.SECONDS));
.build(); admin.declareQueue(queue); CorrelationData cd1 = new CorrelationData(); this.templateWithConfirmsEnabled.convertAndSend("", queue.getName(), "foo", cd1); assertTrue(cd1.getFuture().get(10, TimeUnit.SECONDS).isAck()); CorrelationData cd2 = new CorrelationData(); this.templateWithConfirmsEnabled.convertAndSend("", queue.getName(), "bar", cd2); CorrelationData cd3 = new CorrelationData(); this.templateWithConfirmsEnabled.convertAndSend("NO_EXCHANGE_HERE", queue.getName(), "foo", cd3); assertFalse(cd3.getFuture().get(10, TimeUnit.SECONDS).isAck()); assertThat(cd3.getFuture().get().getReason(), containsString("NOT_FOUND")); CorrelationData cd4 = new CorrelationData("42"); AtomicBoolean resent = new AtomicBoolean(); this.templateWithConfirmsAndReturnsEnabled.setReturnCallback((m, r, rt, e, rk) -> {