1、生产者的消息可靠性投递机制
1)springboot yml文件配置
spring:
rabbitmq:
host: 10.0.23.83
username: lifwe
password: 123456
virtual-host: amq123
port: 5672
publisher-confirm-type: correlated #类型设置为correlate
publisher-returns: true #需要设置weitrue
template:
mandatory: true #设置交换机处理失败消息的模式需要设置为true
2)rabbit exchange和queue配置类
package com.rabbitmq.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author lifwe
* @version 1.0
* @ClassName :RabbitMqConfig
* @Description :
* @date 2021/3/17 10:53
*/
@Configuration
public class RabbitMqConfig {
public static final String MQ_EXCHANGE = "boot_exchange";
public static final String MQ_QUEUE = "boot_queue";
@Bean("mqExchange")
public Exchange getMqExchange(){
return ExchangeBuilder.directExchange(MQ_EXCHANGE).durable(true).build();
}
@Bean("mqQueue")
public Queue getQueue(){
return QueueBuilder.durable(MQ_QUEUE).build();
}
@Bean
public Binding getBinding(@Qualifier("mqExchange") Exchange exchange , @Qualifier("mqQueue") Queue queue){
return BindingBuilder.bind(queue).to(exchange).with("info").noargs();
}
}
3)验证测试Test类
package com.rabbitmq.springbootrabbitmq;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author lifwe
* @version 1.0
* @ClassName :Test
* @Description :
* @date 2021/3/17 11:10
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class Test {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* @Author lifwe
* @Description 测试exchange消息投递方法
* @Date 11:32 2021/3/17
* @Param []
* @return void
**/
@org.junit.Test
public void test() throws InterruptedException {
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
if(b){
System.out.println("exchange接收消息成功!");
}else{
System.out.println("exchange接收消息失败!");
}
}
});
rabbitTemplate.convertAndSend("boot_exchange","info","springboot 测试confirm");
Thread.sleep(3000);
}
/**
* @Author lifwe
* @Description 测试queue消息投递方法
* @Date 11:32 2021/3/17
* @Param []
* @return void
**/
@org.junit.Test
public void test2() throws InterruptedException {
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println(returnedMessage.getReplyText());
}
});
rabbitTemplate.convertAndSend("boot_exchange","info1","springboot 测试confirm");
Thread.sleep(3000);
}
}
4)查看结果
当将routing key 故意写错时,会进入回调方法,只有当消息投递queue失败时,才会进入回调方法
2、消费者手动确认机制
1)yml文件配置
spring:
rabbitmq:
host: 10.0.23.83
username: lifwe
password: 123456
virtual-host: amq123
port: 5672
listener:
direct:
acknowledge-mode: manual
simple:
acknowledge-mode: manual
2)监听代码
@Component
public class RabbitMqListener {
@RabbitListener(queues="boot_queue")
public void getRabbitMqMessage(Message message, Channel channel) throws IOException {
try{
int i = 1/0;
System.out.println(message.getBody());
channel.basicNack(message.getMessageProperties().getDeliveryTag(),true,false);
}catch (Exception e){
System.out.println("手动确认失败");
channel.basicNack(message.getMessageProperties().getDeliveryTag(),true,false);
}
}
}
3、消费端限流
1)yml文件配置
spring:
rabbitmq:
host: 10.0.23.83
username: lifwe
password: 123456
virtual-host: amq123
port: 5672
listener:
direct:
acknowledge-mode: manual #开启限流必须设置为手动确认
prefetch: 2 #每次拉取的条数,消费完才能再次拉取
simple:
acknowledge-mode: manual
prefetch: 2
4、TTL 存活时间
1)在配置类中设置整个队列的过期时间
@Bean("mqQueue")
public Queue getQueue(){
return QueueBuilder.durable(MQ_QUEUE).ttl(5000).build();
}
2)给指定消息设置过期时间
MessageProperties messageProperties = new MessageProperties();
messageProperties.setExpiration("10000"); // 设置过期时间,单位:毫秒
byte[] msgBytes = "测试消息自动过期".getBytes();
Message message = new Message(msgBytes, messageProperties);
rabbitTemplate.convertAndSend("TTL_EXCHANGE", "TTL", message);
当队列过期时间与指定消息过期时间冲突时,以最小的为准
5、死信队列
DLX:Dead Letter Exchange 死信交换机,即当消息dead后从当前队列进入死信交换机
进入死信交换机的三种情况:
1)queue设置了过期时间,当过了指定时间还未消费的消息进入死信队列
2)当消费的消息拒绝签收并不重新返回当前队列的消息进入死信队列
3)当queue设置了最大的记录,当队列消息满了后,再进来的消息直接进入死信队列
代码示例:
public static final String MQ_ORDER_EXCHANGE = "mq_order_exchange";
public static final String MQ_ORDER_QUEUE = "mq_order_queue";
public static final String MQ_DLX_EXCHANGE = "mq_dlx_exchange";
public static final String MQ_DLX_QUEUE = "mq_dlx_queue";
/**
* @return
* @Author lifwe
* @Description 订单队列
* @Date 14:13 2021/3/18
* @Param
**/
@Bean("mqOrderQueue")
public Queue getOrderQueue() {
return QueueBuilder.durable(MQ_ORDER_QUEUE).ttl(10000).deadLetterExchange(MQ_DLX_EXCHANGE).deadLetterRoutingKey("order.dlx").build();
}
/**
* @return org.springframework.amqp.core.Exchange
* @Author lifwe
* @Description 订单交换机
* @Date 14:17 2021/3/18
* @Param []
**/
@Bean("mqOrderExchange")
public Exchange getOrderExchange() {
return ExchangeBuilder.topicExchange(MQ_ORDER_EXCHANGE).durable(true).build();
}
/**
* @return org.springframework.amqp.core.Binding
* @Author lifwe
* @Description 绑定订单交换机和队列
* @Date 14:20 2021/3/18
* @Param [mqOrderQueue, mqOrderExchange]
**/
@Bean
public Binding getOrderBinding(@Qualifier("mqOrderQueue") Queue mqOrderQueue, @Qualifier("mqOrderExchange") Exchange mqOrderExchange) {
return BindingBuilder.bind(mqOrderQueue).to(mqOrderExchange).with("order.info.#").noargs();
}
/**
* @Author lifwe
* @Description 死信队列
* @Date 14:22 2021/3/18
* @Param []
* @return org.springframework.amqp.core.Queue
**/
@Bean("mqDtlQueue")
public Queue getDtlQueue() {
return QueueBuilder.durable(MQ_DLX_QUEUE).build();
}
/**
* @return org.springframework.amqp.core.Exchange
* @Author lifwe
* @Description 死信队列交换机
* @Date 14:17 2021/3/18
* @Param []
**/
@Bean("mqDtlExchange")
public Exchange getDtlExchange() {
return ExchangeBuilder.topicExchange(MQ_DLX_EXCHANGE).durable(true).build();
}
/**
* @Author lifwe
* @Description 死信队列交换机绑定
* @Date 14:24 2021/3/18
* @Param [mqDtlQueue, mqDtlExchange]
* @return org.springframework.amqp.core.Binding
**/
@Bean
public Binding getDTLBinding(@Qualifier("mqDtlQueue") Queue mqDtlQueue, @Qualifier("mqDtlExchange") Exchange mqDtlExchange) {
return BindingBuilder.bind(mqDtlQueue).to(mqDtlExchange).with("order.dlx.#").noargs();
}
测试代码:
/**
* @Author lifwe
* @Description 测试死信队列
* @Date 14:26 2021/3/18
* @Param []
* @return void
**/
@org.junit.Test
public void test3(){
for(int i=0; i<10 ; i++){
rabbitTemplate.convertAndSend("mq_order_exchange", "order.info."+i, "order.info"+i);
}
}
order队列设置了10秒的存活时间,当过了10秒后,进入死信队列,示例图:
6、延迟队列
延迟队列即是TTL+死信队列实现,即消费者消费的是死信队列,基于上面的例子进行测试
/**
* @Author lifwe
* @Description 延时队列消费
* @Date 15:00 2021/3/18
* @Param [message, channel]
* @return void
**/
@RabbitListener(queues="mq_dlx_queue")
public void getDlxMqMessage(Message message, Channel channel) throws IOException {
try{
System.out.println(new String(message.getBody()));
channel.basicNack(message.getMessageProperties().getDeliveryTag(),true,false);
}catch (Exception e){
System.out.println("手动确认失败");
channel.basicNack(message.getMessageProperties().getDeliveryTag(),true,false);
}
}