1. 什么时候应该用MQ?2. Rabbit 基础知识3. docker 安装 RabbitMQ4. Springboot (四) RabbitMQ入门
Springboot (四) RabbitMQ消费者重试机制(spring retry实现)
项目下载地址 Springboot-RabbitMQ-demo
1. 新建配置类 ,上代码
重点在于,retryTemplate 设置重试 。
@Slf4j
@Configuration
public class RabbitRetryConfig {
@Autowired
ConnectionFactory rabbitConnectionFactory;
//@Bean 缓存连接池
//public CachingConnectionFactory rabbitConnectionFactory
@Autowired
RabbitProperties properties;
// 存在此名字的bean 自带的容器工厂会不加载,如果想自定义来区分开 需要改变bean 的名称
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
containerFactory.setConnectionFactory(rabbitConnectionFactory);
// 并发消费者数量
containerFactory.setConcurrentConsumers(1);
containerFactory.setMaxConcurrentConsumers(20);
// 自动应答
containerFactory.setAcknowledgeMode(AcknowledgeMode.AUTO);
//containerFactory.setMessageConverter(new Jackson2JsonMessageConverter());
containerFactory.setChannelTransacted(true);
containerFactory.setAdviceChain(
RetryInterceptorBuilder
.stateless()
.recoverer(new RejectAndDontRequeueRecoverer())
.retryOperations(rabbitRetryTemplate())
.build()
);
return containerFactory;
}
@Bean
public RetryTemplate rabbitRetryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
// 设置监听(不是必须)
retryTemplate.registerListener(new RetryListener() {
@Override
public <T, E extends Throwable> boolean open(RetryContext retryContext, RetryCallback<T, E> retryCallback) {
// 执行之前调用 (返回false时会终止执行)
return true;
}
@Override
public <T, E extends Throwable> void close(RetryContext retryContext, RetryCallback<T, E> retryCallback, Throwable throwable) {
// 重试结束的时候调用 (最后一次重试 )
}
@Override
public <T, E extends Throwable> void onError(RetryContext retryContext, RetryCallback<T, E> retryCallback, Throwable throwable) {
// 异常 都会调用
log.error("-----第{}次调用", retryContext.getRetryCount());
}
});
// 个性化处理异常和重试 (不是必须)
/* Map<Class<? extends Throwable>, Boolean> retryableExceptions = new HashMap<>();
//设置重试异常和是否重试
retryableExceptions.put(AmqpException.class, true);
//设置重试次数和要重试的异常
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(5,retryableExceptions);*/
retryTemplate.setBackOffPolicy(backOffPolicyByProperties());
retryTemplate.setRetryPolicy(retryPolicyByProperties());
return retryTemplate;
}
@Bean
public ExponentialBackOffPolicy backOffPolicyByProperties() {
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
long maxInterval = properties.getListener().getSimple().getRetry().getMaxInterval().getSeconds();
long initialInterval = properties.getListener().getSimple().getRetry().getInitialInterval().getSeconds();
double multiplier = properties.getListener().getSimple().getRetry().getMultiplier();
// 重试间隔
backOffPolicy.setInitialInterval(initialInterval * 1000);
// 重试最大间隔
backOffPolicy.setMaxInterval(maxInterval * 1000);
// 重试间隔乘法策略
backOffPolicy.setMultiplier(multiplier);
return backOffPolicy;
}
@Bean
public SimpleRetryPolicy retryPolicyByProperties() {
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
int maxAttempts = properties.getListener().getSimple().getRetry().getMaxAttempts();
retryPolicy.setMaxAttempts(maxAttempts);
return retryPolicy;
}
}
设计思路,实现消费者拦截器的容器工厂,增加 重试类 来设置重试参数。
本例子,获取了Rabbitproperties 的配置,也可以自定义。 消息在被消费者获取后,重试3次后,将会过期,我设置了死信队列,将重试处理不了的消息 了 路由到了死信队列中 。死信队列的消息需要人工干预处理。
2. 实现例子
我们要新增一个 正常的交换机 (exchangge_normal
) 和 死信交换机 (exchangge_failure),绑定正常队列 (queue_normal)和死信队列(queue_failure)。
开始动手使用RabbitMQ 管理页面 创建队列和交换机, routingkey 统一用 key来表示 。
队列:
正常队列 需要设置 ”x-dead-letter-exchange”参数
死信队列 :
交换机:
交换机和队列关系绑定好之后,我们上一个简单的消费者代码,实现一个简单的异常,让消息无法正常消费。
/**
* 接受指定队列的消息
*/
@RabbitListener(queues = {"queue_normal"})
public void consumer2(Message message, Channel channel) {
// 消息内容
String body = new String(message.getBody());
log.error(body);
int i = 1 / 0;
}
3. 测试代码:
启动消费者进行监听消息,手动在Rabbit管理页面发送一条消息。
队列初始化状态,一条待消费消息。
启动消费者,控制台打印日志,因为我在监听代码中加了次数日志;
查看队列消息数量变化,消息被转发到了死信队列
我们去看看消息的头信, 来源是正常消息队列。例子成功!