我有一个通用的库来处理RabbitMQ的所有配置,我的目标是实现一种通用的策略以在某些尝试后处理错误消息,并且在我心目中的交换可以接收所有死信消息,并且通过使标头具有原始队列名称来区分每个消息。经过多次尝试和重试,这一切。
我不知道如何实现这种方法,您的帮助将有助于实现这一目标!
这是我的一名常客
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "${swift.queue.driverOnlineStatus}", durable = "true"),
exchange = @Exchange(value = "${swift.queue.driverExchange}",
type = ExchangeTypes.HEADERS,
ignoreDeclarationExceptions = "true"),
arguments = {
@Argument(name = "x-match", value = "all"),
@Argument(name = "eventName", value = "${swift.queue.driverOnlineStatus}"),
},
key = ""))
这里是配置
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(value = {ConnectionFactory.class})
public SimpleMessageListenerContainer simpleMessageListenerContainerForAsyncRabbit(ConnectionFactory connectionFactoryfactory,
@Autowired(required = false) ErrorHandler errorHandler,
RabbitTemplate rabbitTemplate) {
log.debug("creating new SimpleMessageListenerContainer ... for async rabbit: {}", rabbitProperties);
SimpleMessageListenerContainer containerF = new SimpleMessageListenerContainer(connectionFactoryfactory);
containerF.setConnectionFactory(connectionFactoryfactory);
if (errorHandler != null) {
containerF.setErrorHandler(errorHandler);
}
//listener retry
Interceptor retryListenerInterceptor = RetryInterceptorBuilder.stateless()
.maxAttempts(rabbitProperties.getListener().getSimple().getRetry().getMaxAttempts())
.backOffOptions(rabbitProperties.getListener().getSimple().getRetry().getInitialInterval(),rabbitProperties.getListener().getSimple().getRetry().getMultiplier(),rabbitProperties.getListener().getSimple().getRetry().getMaxInterval())
.recoverer(new RejectAndDontRequeueRecoverer())
.build();
/*
Note on the two below interceptors
use a non-transactional template for the DLQ
When retries reach the maximum number that massage is dead-lettered
*/
//listener try to republish to error exchange
/*Interceptor retryListenerErrorInterceptor = RetryInterceptorBuilder.stateless()
.maxAttempts(rabbitProperties.getListener().getSimple().getRetry().getMaxAttempts())
.backOffOptions(rabbitProperties.getListener().getSimple().getRetry().getInitialInterval(),rabbitProperties.getListener().getSimple().getRetry().getMultiplier(),rabbitProperties.getListener().getSimple().getRetry().getMaxInterval())
.recoverer(new RepublishMessageRecoverer(rabbitTemplate,innodevProp.getEvent().getErrorExchangeName()))
.build();
*/
//publisher retry
Interceptor retryPublisherInterceptor = RetryInterceptorBuilder.stateless()
.maxAttempts(rabbitProperties.getTemplate().getRetry().getMaxAttempts())
.backOffOptions(rabbitProperties.getTemplate().getRetry().getInitialInterval(),rabbitProperties.getTemplate().getRetry().getMultiplier(),rabbitProperties.getTemplate().getRetry().getMaxInterval())
.recoverer(new RepublishMessageRecoverer(rabbitTemplate,innodevProp.getEvent().getErrorExchangeName()))
.build();
containerF.setAdviceChain(retryListenerInterceptor,retryPublisherInterceptor);
return containerF;
}
我不确定是否需要定义SimpleMessageListenerContainer
,我认为我们应该依靠@EnableRabbit
我了解您的方法,我们应该为retryListenerInterceptor
创建一个具有RepublishMessageRecoverer
并且可以完成工作的Bean
答案 0 :(得分:0)
RepublishMessageRecoverer
完全可以满足您的需求。传递重试用尽后,它将消息发送到带有以下附加头的DLX:
public static final String X_EXCEPTION_STACKTRACE = "x-exception-stacktrace";
public static final String X_EXCEPTION_MESSAGE = "x-exception-message";
public static final String X_ORIGINAL_EXCHANGE = "x-original-exchange";
public static final String X_ORIGINAL_ROUTING_KEY = "x-original-routingKey";
您可以将所有消息路由到同一队列,也可以将DLQ与原始路由密钥绑定。
RepublishMessageRecoverer
在消息头中发布消息以及其他信息,例如异常消息,堆栈跟踪,原始交换和路由键。通过创建子类并覆盖additionalHeaders()
,可以添加其他标题。也可以在additionalHeaders()
方法中更改deliveryMode(或其他任何属性):
答案 1 :(得分:0)
通过添加以下属性,我发现spring-boot对简单容器具有开箱即用的重试支持:
spring.rabbitmq.listener.simple.retry.enabled=true
这将启用重试功能,如果您未设置其他与重试相关的属性将采用默认值。另外,您应该确保禁用重新排队功能
spring.rabbitmq.listener.simple.default-requeue-rejected=false
现在您应该通过添加以下代码来定义RepublishMessagRecoverer
@BeanmatchIfMissing = true)
public RepublishMessageRecoverer republishMessageRecoverer(RabbitTemplate rabbitTemplate) {
return new RepublishMessageRecoverer(rabbitTemplate,"error-exchange");
}
@Bean
public RetryOperationsInterceptor rabbitRetryInterceptor(RabbitTemplate rabbitTemplate) {
log.debug("creating retry operation for rabbitmq ");
return RetryInterceptorBuilder.stateless()
.recoverer(republishMessageRecoverer(rabbitTemplate))
.build();
}
现在所有默认容器都将具有此配置,并且如果重试已用尽,则消息将通过这些标头发送到retryInterceptor
中定义的交换机
public static final String X_EXCEPTION_STACKTRACE = "x-exception-stacktrace";
public static final String X_EXCEPTION_MESSAGE = "x-exception-message";
public static final String X_ORIGINAL_EXCHANGE = "x-original-exchange";
public static final String X_ORIGINAL_ROUTING_KEY = "x-original-routingKey";
附加说明:
如果您希望其他队列具有不同的行为,则可以定义另一个listenerContainer
并将其分配给侦听器
**示例:**
@Bean
RabbitListenerContainerFactory dispatchConnection(ConnectionFactory connectionFactory){
System.out.println("creating the container");
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setDefaultRequeueRejected(true);
return factory;
}
并按照以下示例将容器分配给@RabbitListener
@RabbitListener(containerFactory ="beanListenerContainerName", queues = "queueName")
我还要感谢加里·罗素(Gary Russell)的回答。