SpringBoot中配置延迟队列
1.下载好对应的插件到rabbitmq中后添加依赖c3f4b2090b92c315aae2c91.png)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
此时如果通过UI界面可以看到延迟交换机类型 x-delayed-message
2.配置yaml
server:
port: 8080
spring:
application:
# 微服务中会用到,养成好习惯加上。
name: rabbitmq-test-delayDemo
rabbitmq:
host: 127.0.0.1
port: 5672
username: admin
password: admin
virtual-host: /admin #有时候测试、上线、开发都用同一个队列,所以用virtual-host隔离开
listener:
simple:
acknowledge-mode: manual #设置为手动回复的方式
template:
mandatory: true #强制调用回调函数
publisher-returns: true
publisher-confirm-type: CORRELATED #这个表示会调用confirm的回调函数
**关于回复方式:**自动回复的情况下即队列发送消息给消费者就返回ack,可能会造成消息丢失,例如如果消费者没有正确处理好消息。
3.配置MQConfig
@Configuration
public class RabbitMQConfig implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnsCallback {
private static final Logger logger= LoggerFactory.getLogger(RabbitMQConfig.class);
public static final String LAZY_EXCHANGE="dingDanLYEXC";
public static final String LAZY_QUEUE="dingDanLYQUE";
public static final String LAZY_KEY="lazy.#";
@Autowired
RabbitTemplate rabbitTemplate;
@Bean
public CustomExchange lazyExchange(){
Map<String,Object> pros=new HashMap<String,Object>();
pros.put("x-delayed-type","topic"); //指定交换机类型,Topic通配符模式
CustomExchange exchange=new CustomExchange(LAZY_EXCHANGE,"x-delayed-message",true,false,pros) ;
// exchange.setDelayed(true);
//这和props.put("x-delayed-message","topic")是一样的。
return exchange;
}
@Bean
public Queue lazyQueue(){
return new Queue(LAZY_QUEUE,true);
}
@Bean
public Binding lazyBinding(){
return BindingBuilder
.bind(lazyQueue())
.to(lazyExchange())
.with(LAZY_KEY)
.noargs();
}
@PostConstruct
public void init(){
rabbitTemplate.setMandatory(true);//声明了之后才会调用回调函数。
rabbitTemplate.setConfirmCallback((RabbitTemplate.ConfirmCallback) this);
rabbitTemplate.setReturnsCallback((RabbitTemplate.ReturnsCallback) this);
}
//不论消息是否可以找到对应的交换机都会回调。
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
logger.info("消息到交换机是否成功:"+b+" CorrelationData="+correlationData);
}
//消息从交换机到队列如果失败会回调此函数。
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
logger.info("消息到队列是否成功");
}
}
RabbitConfig实现了RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnsCallback接口的confirm方法和reuturnedMessage方法。
关于这两者返回的区别
- ConfirmCallback:确认消息到达交换机,不论是否成功到达都会调用函数,以boolean b确认是否正确到达
- returnedMessage:确认是否正常到达队列,如果没有到达则会调用此方法。
这两种机制是为了提高RabbitMQ的高可用。
4.配置生产者
@Component
public class MQSender{
@Resource
RabbitTemplate rabbitTemplate;
public void sendLazy(Object message){
CorrelationData correlationData=new CorrelationData("123"+new Date());
rabbitTemplate.convertAndSend(RabbitMQConfig.LAZY_EXCHANGE, "lazy.boot", message, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
//设置消息持久化
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
//设置延迟时间
message.getMessageProperties().setDelay(6000);
return message;
}
},correlationData);
}
}
设置延迟时间还有另外一种方式,类似于pros中加"x-delay"
- 关于CorrelationData: 是RabbitMQ的一种高级特性,主要作用是:因为在confirm和returnMessage机制中,为了避免消息丢失,需要给每个消息指定一个唯一的ID,就是CorrelationData
5.配置消费者
@Component
public class TopicConsumer {
private static final Logger logger= LoggerFactory.getLogger(TopicConsumer.class);
@RabbitHandler
@RabbitListener(bindings={@QueueBinding(
value=@Queue(value= RabbitMQConfig.LAZY_QUEUE,durable="true"),
exchange=@Exchange(value=RabbitMQConfig.LAZY_EXCHANGE,durable="true",type="x-delayed-message"),
key=RabbitMQConfig.LAZY_KEY)})
public void consumer1(Channel channel, Message msg) throws IOException {
logger.info("1--消费者收到的消息是:"+new String(msg.getBody()));
// logger.info("路由key为"+msg.getMessageProperties().getReceivedRoutingKey());
// logger.info("交换机为:"+msg.getMessageProperties().getReceivedExchange());
long delTag=msg.getMessageProperties().getDeliveryTag();
logger.info("1--消息id为:"+ delTag);
logger.info("1--message的id为:"+msg.getMessageProperties().getMessageId());
channel.basicAck(delTag,true);//正常确认,第二个参数multiple表示是否批量确认消息
}
@RabbitHandler
@RabbitListener(bindings={@QueueBinding(
value=@Queue(value=RabbitMQConfig.LAZY_QUEUE,durable="true"),
exchange=@Exchange(value=RabbitMQConfig.LAZY_EXCHANGE,durable="true",type="x-delayed-message"),
key=RabbitMQConfig.LAZY_KEY)})
public void consumer2(Channel channel,Message msg) throws IOException {
logger.info("2--消费者收到的消息是:"+new String(msg.getBody()));
// logger.info("路由key为"+msg.getMessageProperties().getReceivedRoutingKey());
// logger.info("交换机为:"+msg.getMessageProperties().getReceivedExchange());
long delTag=msg.getMessageProperties().getDeliveryTag();
logger.info("2--消息id为:"+ delTag);
logger.info("2--message的id为:"+msg.getMessageProperties().getMessageId());
channel.basicNack(delTag,true,false) ;//正常确认,第三个参数表示是否会返回队列中
}
}
- 消费者这里@RabbitListener中的一坨是为了保证如果消费者先运行的时候如果交换机和队列没有的话就创建