GH-292: Fix republishToDlq When no DLX

Resolves https://github.com/spring-cloud/spring-cloud-stream-binder-rabbit/issues/292

When `republishToDlq` is true and `autoBindDlq` is false, there is no dead-letter
exchange to publish to (unless the user has manually provisioned it).

Detect this condition and disable `republishToDlq` with a warning log.
This commit is contained in:
Gary Russell
2020-06-02 15:38:46 -04:00
committed by Oleg Zhurakousky
parent 4b1eabb226
commit 2c948e6a27
2 changed files with 106 additions and 2 deletions

View File

@@ -605,8 +605,7 @@ public class RabbitMessageChannelBinder extends
this.template.setUsePublisherConnection(true);
}
private final String exchange = deadLetterExchangeName(
properties.getExtension());
private final String exchange = deadLetterExchangeName(properties.getExtension());
private final String routingKey = properties.getExtension()
.getDeadLetterRoutingKey();
@@ -616,6 +615,8 @@ public class RabbitMessageChannelBinder extends
private int maxStackTraceLength = -1;
private Boolean dlxPresent;
@Override
public void handleMessage(org.springframework.messaging.Message<?> message) throws MessagingException {
Message amqpMessage = StaticMessageHeaderAccessor.getSourceData(message);
@@ -628,6 +629,9 @@ public class RabbitMessageChannelBinder extends
logger.error("No raw message header in " + message);
}
else {
if (!checkDlx()) {
return;
}
Throwable cause = (Throwable) message.getPayload();
if (!shouldRepublish(cause)) {
if (logger.isDebugEnabled()) {
@@ -692,6 +696,30 @@ public class RabbitMessageChannelBinder extends
}
}
private boolean checkDlx() {
if (this.dlxPresent == null) {
if (properties.getExtension().isAutoBindDlq()) {
this.dlxPresent = Boolean.TRUE;
}
else {
this.dlxPresent = this.template.execute(channel -> {
String dlx = deadLetterExchangeName(properties.getExtension());
try {
channel.exchangeDeclarePassive(dlx);
return Boolean.TRUE;
}
catch (IOException e) {
logger.warn("'republishToDlq' is true, but the '"
+ dlx
+ "' dead letter exchange is not present; disabling 'republishToDlq'");
return Boolean.FALSE;
}
});
}
}
return this.dlxPresent;
}
/**
* Traverse the cause tree, stopping at AmqpRejectAndDontRequeueException
* or ImmediateAcknowledgeAmqpException.

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2020-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.stream.binder.rabbit;
import java.io.IOException;
import java.util.Collections;
import com.rabbitmq.client.Channel;
import org.junit.Test;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.cloud.stream.binder.ExtendedConsumerProperties;
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitConsumerProperties;
import org.springframework.cloud.stream.provisioning.ConsumerDestination;
import org.springframework.integration.IntegrationMessageHeaderAccessor;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.support.ErrorMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Gary Russell
* @since 3.0.6
*
*/
public class RepublishUnitTests {
@Test
public void testBadRepublishSetting() throws IOException {
ConnectionFactory cf = mock(ConnectionFactory.class);
Connection conn = mock(Connection.class);
given(cf.createConnection()).willReturn(conn);
Channel channel = mock(Channel.class);
given(channel.isOpen()).willReturn(true);
given(channel.exchangeDeclarePassive("DLX")).willThrow(new IOException());
given(conn.createChannel(false)).willReturn(channel);
RabbitProperties props = new RabbitProperties();
RabbitMessageChannelBinder binder = new RabbitMessageChannelBinder(cf, props, null);
RabbitConsumerProperties extension = new RabbitConsumerProperties();
ExtendedConsumerProperties<RabbitConsumerProperties> bindingProps =
new ExtendedConsumerProperties<RabbitConsumerProperties>(extension);
MessageHandler handler = binder.getErrorMessageHandler(mock(ConsumerDestination.class), "foo", bindingProps);
ErrorMessage message = new ErrorMessage(new RuntimeException("test"),
Collections.singletonMap(IntegrationMessageHeaderAccessor.SOURCE_DATA,
new Message("foo".getBytes(), new MessageProperties())));
handler.handleMessage(message);
handler.handleMessage(message);
verify(channel, times(1)).exchangeDeclarePassive("DLX");
verify(channel, never()).basicPublish(any(), any(), eq(false), any(), any());
}
}