@Component
public class DlxListener implements ChannelAwareMessageListener {
@RabbitListener(queues = "dead_boot_queue_ttl") //监听队列(设置好队列名)
@Override
public void onMessage(Message message, Channel channel) throws Exception {
try {
//1、接收消息(默认拉去所有消息)
System.out.println(new String(message.getBody()));
//2、业务处理
System.out.println("这里进行业务处理...");
/**
* 注意需要设置 requeue:false 才会进入死信队列
*/
//3、签收消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
} catch (Exception e) {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), true,false);
}
}
}
@Configuration
public class RabbitMQConfig {
//public static final String EXCHANGE_NAME = "boot_topics_exchange";
public static final String EXCHANGE_NAME = "boot_exchange_ttl";
//public static final String QUEUE_NAME = "boot_queue";
//public static final String QUEUE_NAME = "boot_confirm";
// public static final String QUEUE_NAME = "boot_Qos";
public static final String QUEUE_NAME = "boot_queue_ttl";
public static final String ROUTING_KEY = "boot.#";
public static final String DEAD_EXCHANGE_NAME = "dead_boot_exchange_ttl";
public static final String DEAD_QUEUE_NAME = "dead_boot_queue_ttl";
//1、交换机
@Bean("bootExchange")
public Exchange bootExchange() {
return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();
}
//2、队列
@Bean("bootQueue")
public Queue bootQueue() {
//.ttl(10000) 设置所有队列消息10秒后过期
/**
* public QueueBuilder ttl(int ttl) {
* return withArgument("x-message-ttl", ttl);
* }
* 封装了"x-message-ttl"为key,手动设置过期时间value
*/
//return QueueBuilder.durable(QUEUE_NAME).ttl(100000).build();
return QueueBuilder.durable(QUEUE_NAME)
.deadLetterExchange(DEAD_EXCHANGE_NAME) //绑定死信交换机的名称
.deadLetterRoutingKey("dead_boot.Hello")
.ttl(20000) //设置队列过期事件 ,过期后进入死信队列
.maxLength(10) //设置队列最大容量,超过后进入死信队列
.build();
}
//死信交换机
@Bean("deadExchange")
public Exchange deadExchange() {
return ExchangeBuilder.topicExchange(DEAD_EXCHANGE_NAME).durable(true).build();
}
//2、队列
@Bean("deadQueue")
public Queue deadQueue() {
return QueueBuilder.durable(DEAD_QUEUE_NAME).build();
}
/**
* 1、知道那个队列
* 2、知道那个交换机
* 3、routing key
*/
@Bean
public Binding bindQueueExchange(@Qualifier("bootQueue") Queue queue, @Qualifier("bootExchange") Exchange exchange) {
//绑定交换机与队列
return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY).noargs();
}
/**
* 绑定死信交换机
*/
@Bean
public Binding bindDeadQueueExchange(@Qualifier("deadQueue") Queue queue, @Qualifier("deadExchange") Exchange exchange) {
//绑定死信交换机与队列
return BindingBuilder.bind(queue).to(exchange).with("dead_boot.#").noargs();
}
}
1、消息可靠投递
因为rabbitmq默认为自动消息确认,需要手动在配置文件中开启模式
1.1、confirm:确认模式
服务提供者与交换机之间的确认模式
1、在.yml
配置文件中添加spring: rabbitmq: publisher-confirm-type: correlated
2、使用rabbitTemplate
的.setConfirmCallback()
方法,里面传入ConfirmCallback()
匿名内部类的方式重写confirm
方法
3、参数说明见代码注释
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* Confirm消息确认
*/
@Test
public void test1() {
/**
* setConfirmCallback设置confirm消息确认
* 1、确认模式开启:publisher-confirm-type: correlated
* 2、rabbitTemplate中定义setConfirmCallback()
*/
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
/**
* 参数说明 (@Nullable CorrelationData correlationData, boolean ack, @Nullable String cause)
* correlationData:消息唯一标识符
* ack: exchange交换机是否成功接收到消息 成功为true 失败为false
* cause:失败原因
*/
System.out.println("方法执行了");
if (ack) {
System.out.println("消息接收成功");
} else {
System.out.println("消息接受失败:"+cause);
//可以在这里进行失败的业务处理
}
}
});
rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME, "boot.confirm", "Message Confirm");
}
2.2、return:退回模式
交换机与队列的确认模式
1、在.yml
配置文件中添加spring: rabbitmq: publisher-returns: true
2、使用rabbitTemplate
的.setReturnsCallback()
方法,里面传入ReturnsCallback()
匿名内部类的方式重写confirm
方法
3、参数说明见代码注释
/**
* Return回退模式:消息发送到Exchange,若Exchange路由到queue失败,则会调用ReturnsCallback
* 1、publisher-returns: true
* 2、设置ReturnsCallback
* 3、设置处理方式:
* 1、若失败,丢失消息(默认)
* 2、若失败,返回消息给ReturnsCallback进行处理
*/
@Test
public void test2() {
//设置失败处理
rabbitTemplate.setMandatory(true);
//若交换机消息路由到队列失败会调用
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returned) {
/**
* returned.
* private final Message message; 消息对象
* private final int replyCode; 错误码
* private final String replyText; 错误信息
* private final String exchange; 交换机
* private final String routingKey; 路由key
*/
System.out.println("执行了");
}
});
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME, "boot.confirm", "Message Confirm1");
}
}
2.3Consumer ACK确认模式
消费端确认模式,设置为手动确认(默认为自动确认)
1、首先设置 simple: acknowledge-mode: manual
2、实现ChannelAwareMessageListener
接口重写onMessage()
方法
/**
* Consumer ACK
*/
@Component
public class RabbitMQListener1 implements ChannelAwareMessageListener {
/**
* 手动签收
* 1、listener:simple:acknowledge-mode: manual #手动签收
* 2、监听器继承ChannelAwareMessageListener
* 3、消息成功:
*/
//@RabbitListener(queues = "boot_confirm")
@Override
public void onMessage(Message message, Channel channel) throws Exception {
Thread.sleep(2000);
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
// System.out.println(new String(message.getBody()));
System.out.println("进行业务处理");
/**
* void basicAck(long deliveryTag, boolean multiple) throws IOException;
* deliveryTag:收到的消息的标签
* multiple:是否接收多条消息
*/
int i = 3 / 0;
channel.basicAck(deliveryTag,true);
} catch (Exception e) {
/**
*void basicNack(long deliveryTag, boolean multiple, boolean requeue)
*
* requeue:重回队列,若发送失败,消息重回queue,broker重新发送给consumer
*/
System.out.println("业务处理失败");
channel.basicNack(deliveryTag,true,true);
}
}
}
2、消费端限流
1、首先设置消费端为手动签收listener: simple: acknowledge-mode: manual
2、只需要在配置文件中文件添加spring: rabbitmq: prefetch: 1
来配置每次接收的队列信息数(数字),每当消费端手动签收后再接收下一条消息
3、TTL
/**
* 设置ttl 消息的过期时间
* 1、消息队列统一过期
* return QueueBuilder.durable(QUEUE_NAME).ttl(10000).build();
* 2、消息单独过期
* new MessagePostProcessor()
* 若设置了队列过期时间和消息单独过期时间,按时间短的为准
* 消息过期时间到了,只有消息在队列顶端,才会去判断其是否过期
*/
@Test
public void test3() {
for (int i = 0; i < 10; i++) {
//此时消息并不在队列顶端,不会单独过期!
if (i == 5) {
rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME, "boot.ttl", "test ttl rabbitmq", new MessagePostProcessor() {
/**
* 消息后处理对象,设置一些参数的信息
*/
@Override
public Message postProcessMessage(Message message) throws AmqpException {
//设置message的信息
message.getMessageProperties().setExpiration("5000"); //消息的过期时间
//返回该消息
return message;
}
});
} else {
//若设置了队列过期,所有消息等待过期
rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME, "boot.ttl", "test ttl rabbitmq");
}
}
}
4、死信队列
消息成为死信的三种情况:
1、TTL设置时间到了
2、超过了设置的队列长度
3、消费方拒接签收消息
/**
* 进入死信队列条件
* 1、超过队列长度
* 2、过期时间到期
* 3、消费者拒绝接受
*/
@Test
public void test4() {
//测试1:测试正常队列过期时间
rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME, "boot.test", "测试死信队列...");
//测试2:测试长度限制
// for (int i = 0; i < 20; i++) {
// rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME, "boot.test", "测试死信队列...");
// }
//测试3:消费者消息拒收
// rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME, "boot.test", "测试死信队列...");
}
5、延迟队列
6、消息追踪
方式1:
rabbitmqctl trace_on
rabbitmqctl trace_off
方式2:
#打开插件
rabbitmq-plugins list
rabbitmq-plugins enable rabbit_tracing