SpringBoot整合RabbitMQ高级特性

基础

https://blog.csdn.net/qq_41520636/article/details/115473826

进阶

https://blog.csdn.net/qq_41520636/article/details/115562164

消息可靠性投递

pom引入

  <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
        <version>2.1.4.RELEASE</version>
  </dependency>

生产端的application.yml设置

spring:
  rabbitmq:
    # 消息可靠性投递
    publisher-returns: true
    # 消息可靠性投递
    publisher-confirms: true
    # 交换机处理消息失败的模式
    template:
      mandatory: true

 配置类

package com.hikktn.config;

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;


/**
 * @ClassName RabbitMQConfig
 * @Description TODO
 * @Author lisonglin
 * @Date 2021/4/9 14:49
 * @Version 1.0
 */
@Configuration
public class RabbitMQConfig {

	@Autowired
	private CachingConnectionFactory connectionFactory;

    // 路由模式
	public static final String DIRECT_QUEUE_NAME_1 = "routing_direct_queue_1";
	public static final String DIRECT_QUEUE_NAME_2 = "routing_direct_queue_2";
	public static final String TEST_DIRECT_EXCHANGE = "direct_exchange";
	private static final String DIRECT_ROUTING_KEY_INSERT = "insert";
	private static final String DIRECT_ROUTING_KEY_UPDATE = "update";

	@Bean
	public RabbitTemplate rabbitTemplate(){
		// 使用confirm-callback ,必须配置publisherConfirms 为true
		connectionFactory.setPublisherConfirms(true);
		// 使用return-callback,必须配置publisherReturns为true
		connectionFactory.setPublisherReturns(true);
		RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
		// 使用return-callback时必须设置mandatory为true,设置交换机处理失败消息的模式
		rabbitTemplate.setMandatory(true);
        // 确认模式
		rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
			@Override
			public void confirm(CorrelationData correlationData, boolean ack, String cause) {
				System.out.println("消息唯一标识:" + correlationData);
				System.out.println("确认结果:" + ack);
				System.out.println("失败原因:" + cause);
				if (!ack) {
					System.err.println("异常处理...");
			    }
		    }
	    });

		// 回退模式: 当消息发送给Exchange后,Exchange路由到Queue失败是 才会执行 ReturnCallBack
		rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
			@Override
			public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
				System.err.println("消息丢失:return exchange: " + exchange + ", routingKey: " + routingKey + ", replyCode" +
						": " + replyCode + ", replyText: " + replyText);
			}
		});
    	return rabbitTemplate;
    }

    // 路由模式
	@Bean("routing_direct_queue_1")
	public Queue createRoutingQueue1() {
		return new Queue(DIRECT_QUEUE_NAME_1, true, false, false, null);
	}

	@Bean("routing_direct_queue_2")
	public Queue createRoutingQueue2() {
		return new Queue(DIRECT_QUEUE_NAME_2, true, false, false, null);
	}

    // 队列与交换机进行绑定
	@Bean
	public Binding bindingQueueAndDirectExchange1(@Qualifier("routing_direct_queue_1") Queue queue, @Qualifier("direct_exchange") DirectExchange exchange) {
		return BindingBuilder.bind(queue).to(exchange).with(DIRECT_ROUTING_KEY_INSERT);
	}

	@Bean
	public Binding bindingQueueAndDirectExchange2(@Qualifier("routing_direct_queue_2") Queue queue, @Qualifier("direct_exchange") DirectExchange exchange) {
		return BindingBuilder.bind(queue).to(exchange).with(DIRECT_ROUTING_KEY_UPDATE);
	}
}

生产者

package com.hikktn.producer;

import com.hikktn.config.RabbitMQConfig;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @ClassName Sender
 * @Description 生产者
 * @Author lisonglin
 * @Date 2021/4/9 16:54
 * @Version 1.0
 */
@Component
public class Sender {
	@Autowired
	private RabbitTemplate rabbitTemplate;

    /**
	 * 生产者1 (路由模式)
	 *
	 * @param routingKey
	 * @param message
	 */
	public void sendDirectTest1(String routingKey, String message) {
		CorrelationData correlationData = new CorrelationData("1234567890"); // id + 时间戳 全局唯一
		rabbitTemplate.convertAndSend(RabbitMQConfig.TEST_DIRECT_EXCHANGE, routingKey, message, correlationData);
	}

	/**
	 * 生产者2 (路由模式)
	 *
	 * @param routingKey
	 * @param msg
	 */
	public void sendDirectTest2(String routingKey, String msg) {
		CorrelationData correlationData = new CorrelationData("0123456789"); // id + 时间戳 全局唯一
		rabbitTemplate.convertAndSend(RabbitMQConfig.TEST_DIRECT_EXCHANGE, routingKey, msg, correlationData);
	}
}

消费者1

package com.hikktn.listener;
 
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
 
/**
 * @ClassName DirectListener1
 * @Description TODO
 * @Author lisonglin
 * @Date 2021/4/9 21:55
 * @Version 1.0
 */
@Component
public class DirectListener1 {
 
	@RabbitListener(queues = "routing_direct_queue_1")
	public void receive(String message){
		System.out.println("消费者1接收到的消息为:" + message);
	}
}

 消费者2

package com.hikktn.listener;
 
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
 
/**
 * @ClassName DirectListener1
 * @Description TODO
 * @Author lisonglin
 * @Date 2021/4/9 21:55
 * @Version 1.0
 */
@Component
public class DirectListener2 {
 
	@RabbitListener(queues = "routing_direct_queue_2")
	public void receive(String message){
		System.out.println("消费者1接收到的消息为:" + message);
	}
}

控制器

package com.hikktn.controller;
 
import com.hikktn.producer.Sender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
 
/**
 * @ClassName ConsumerController
 * @Description TODO
 * @Author lisonglin
 * @Date 2021/4/9 19:27
 * @Version 1.0
 */
@RestController
public class ConsumerController {
	@Autowired
	private Sender sender;
 
    @GetMapping("/getDirect")
	public String sendDirect(){
		sender.sendDirectTest1("insert","direct模式1,发送了消息");
		sender.sendDirectTest2("update","direct模式2,发送了消息");
		return "ok";
	}
}

 测试confirm  ----- 交换机名改为没有创建的交换机,造成找不到交换机的错误。

	/**
	 * 生产者1 (路由模式)
	 *
	 * @param routingKey
	 * @param message
	 */
	public void sendDirectTest1(String routingKey, String message) {
		MessageProperties messageProperties = new MessageProperties();
		//在生产环境中这里不用Message,而是使用 fastJson 等工具将对象转换为 json 格式发送
		Message msg = new Message(message.getBytes(), messageProperties);
		CorrelationData correlationData = new CorrelationData("1234567890"); // id + 时间戳 全局唯一
		rabbitTemplate.convertAndSend("direct_exchange123", routingKey, msg, correlationData);
	}

测试 returned ----  路由key值改为没有创建的routingKey,造成找不到routingKey的错误。

    /**
	 * 生产者1 (路由模式)
	 *
	 * @param routingKey
	 * @param message
	 */
	public void sendDirectTest1(String routingKey, String message) {
		CorrelationData correlationData = new CorrelationData("1234567890"); // id + 时间戳 全局唯一
		rabbitTemplate.convertAndSend(RabbitMQConfig.TEST_DIRECT_EXCHANGE, "update123", message, correlationData);
	}

确认模式和回退模式区别

确认模式:监听交换机Exchange是否正常收到消息,正常收到消息,可以进行回调函数,后续业务处理;异常情况,同样也能够进行处理。

回退模式:监听队列Queue是否正常收到消息,正常收到消息,默认不处理;异常收到消息,拦截请求,进行重新发送队列。

Consumer ACK

消费者application.yml

spring:
  rabbitmq:
    listener:
      simple:
        # 消息传递(ack是否手动确认,none:不确认,auto:自动确认,manual:手动确认)
        acknowledge-mode: manual

手动确认前和手动确认后 

改造一下消费者1和消费者2

消费者1

package com.hikktn.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @ClassName DirectListener1
 * @Description TODO
 * @Author lisonglin
 * @Date 2021/4/9 21:55
 * @Version 1.0
 */
@Component
public class DirectListener1 {

	@RabbitListener(queues = "routing_direct_queue_1")
	public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
		System.out.println("direct消费者1接收到的消息为:" + message);
		try {

			/**
			 * 无异常就确认消息
			 * basicAck(long deliveryTag, boolean multiple)
			 * deliveryTag:取出来当前消息在队列中的的索引;
			 * multiple:为true的话就是批量确认,如果当前deliveryTag为5,那么就会确认
			 * deliveryTag为5及其以下的消息;一般设置为false
			 */
			channel.basicAck(tag, false);
		}catch (Exception e){
			/**
			 * 有异常就绝收消息
			 * basicNack(long deliveryTag, boolean multiple, boolean requeue)
			 * requeue:true为将消息重返当前消息队列,还可以重新发送给消费者;
			 *         false:将消息丢弃
			 */
			channel.basicNack(tag,false,true);
		}
	}
}

消费者2 

package com.hikktn.listener;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @ClassName DirectListener1
 * @Description TODO
 * @Author lisonglin
 * @Date 2021/4/9 21:55
 * @Version 1.0
 */
@Component
public class DirectListener2 {

	@RabbitListener(queues = "routing_direct_queue_2")
	public void receive(String message){
		System.out.println("direct消费者2接收到的消息为:" + message);
	}
}

 测试

 可以非常清楚地看到两个队列里的消息,有一条消息没有确认,还在运行中....

 当选择手动确认后,必须加上channel.basicAck(tag, false);这句代码来确认结果正常终了,否则每次重启Spring服务,该消息又会重复发送。

channel.basicNack(tag,false,true);则会重复发送消息,直到确认消息为止。

测试

package com.hikktn.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @ClassName DirectListener1
 * @Description TODO
 * @Author lisonglin
 * @Date 2021/4/9 21:55
 * @Version 1.0
 */
@Component
public class DirectListener2 {

	@RabbitListener(queues = "routing_direct_queue_2")
	public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
		System.out.println("direct消费者2接收到的消息为:" + message);
		// 手动否认
		channel.basicNack(tag,false,true);
	}
}

结果,消息一直在发送

消费端限流和并发消费

spring:
  rabbitmq:
    listener:
      simple:
        # 消息传递(ack是否手动确认,none:不确认,auto:自动确认,manual:手动确认)
        acknowledge-mode: manual
        # 消费端限流(处理消息的限制)
        prefetch: 1
        # 接收的消费队列数(并发)
        concurrency: 5

测试

	@RabbitListener(queues = "simple_queue")
	public void receive(Message message, Channel channel) {
		String messageRec = new String(message.getBody());
		System.out.println("simple模式接收到了消息:"+messageRec);
		// try {
		// 	channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
		// } catch (IOException e) {
		// 	System.out.println("报错了------------------"+e.getMessage());
		// }
	}
	@GetMapping("/getSimple")
	public String sendSimple(){
		for (int i = 0; i < 10; i++) {
			sender.sendSimpleTest("simple模式,发送了消息");
		}
		return "ok";
	}

 结果

 一次收到五条消息,但是消费的消息条数,其实是一条一条处理的,只是开启了并发。所以才一次性接收5条消息。

TTL

设置参数

x-message-ttl:单位:ms(毫秒),会对整个队列消息统一过期。

expiration:单位:ms(毫秒),当该消息在队列头部时(消费时),会单独判断这一消息是否过期。

如果这两个都设置了,以时间短的为准。

第一种方式

 配置类

	@Bean("topic_queue_2")
	public Queue createTopicQueue2() {
		Map<String, Object> args = new HashMap<>();
		// x-message-ttl指队列的过期时间
		// 队列中的消息未被消费则30秒后过期 TTL(指定队列的消息过期时间)
		args.put("x-message-ttl", 30000);
		return QueueBuilder.durable(TOPIC_QUEUE_NAME_2).withArguments(args).build();
	}

 取消消费者类

// @RabbitListener(queues = "topic_queue_1")
	// @RabbitHandler
	// public void receive1(Message message, Channel channel, String msg) throws IOException {
	// 	long deliveryTag = message.getMessageProperties().getDeliveryTag();
	// 	System.out.println("消费者1接收到的消息为:" + msg);
	// 	String messageRec = new String(message.getBody());
	// 	System.out.println("消费者1接收到的消息为:" + messageRec);
	// 	try {
	// 		// 手动签收
	// 		channel.basicAck(deliveryTag, true);
	// 	} catch (IOException e) {
	// 		System.out.println("报错了------------------"+e.getMessage());
	// 		// 拒绝签收,不重回队列 requeue=false
	// 		channel.basicNack(deliveryTag,true,false);
	// 	}
	// }

	// @RabbitListener(queues = "topic_queue_2")
	// public void receive2(Message message, Channel channel) {
	// 	String messageRec = new String(message.getBody());
	// 	System.out.println("消费者2接收到的消息为:" + messageRec);
	// 	try {
	// 		channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
	// 	} catch (IOException e) {
	// 		System.out.println("报错了------------------" + e.getMessage());
	// 	}
	// }

取消回调方法和回退方法

	// @Bean
	// public RabbitTemplate rabbitTemplate(){
	// 	//若使用confirm-callback ,必须要配置publisherConfirms 为true
	// 	connectionFactory.setPublisherConfirms(true);
	// 	//若使用return-callback,必须要配置publisherReturns为true
	// 	connectionFactory.setPublisherReturns(true);
	// 	RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
	// 	//使用return-callback时必须设置mandatory为true,或者在配置中设置mandatory-expression的值为true
	// 	rabbitTemplate.setMandatory(true);
	// 	// 如果消息没有到exchange,则confirm回调,ack=false; 如果消息到达exchange,则confirm回调,ack=true
	// 	rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
	// 		@Override
	// 		public void confirm(CorrelationData correlationData, boolean ack, String cause) {
	// 			System.out.println("消息唯一标识:" + correlationData);
	// 			System.out.println("确认结果:" + ack);
	// 			System.out.println("失败原因:" + cause);
	// 			if (!ack) {
	// 				System.err.println("异常处理...");
	// 			}
	// 		}
	// 	});
	//
	// 	//如果exchange到queue成功,则不回调return;如果exchange到queue失败,则回调return(需设置mandatory=true,否则不回回调,消息就丢了)
	// 	rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
	// 		@Override
	// 		public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
	// 			System.err.println("消息丢失:return exchange: " + exchange + ", routingKey: " + routingKey + ", replyCode" +
	// 					": " + replyCode + ", replyText: " + replyText);
	// 		}
	// 	});
	//
	// 	return rabbitTemplate;
	// }

取消发生队列1 的消息

	@GetMapping("/getTopic")
	public String sendTopic(){
		// sender.sendTopicTest1("1213457107.@qq.com","topic模式1,发送给1213457107@qq.com消息");
		sender.sendTopicTest2("sms.@qq.com","topic模式2,发送给sms.@qq.com消息");
		return "ok";
	}

 发送消息

消息处于没有接收状态

未没有发送消息前的状态 

发送后,topic_queue_2队列30秒钟后过期

 30秒钟过期

问题 

 但是有一问题,明明我并没有发送topic_queue_1消息,可是topic_queue_1队列自从发送了消息后,就一直处于ready状态,这是我不明白的点。

 第二种方式

	/**
	 * 生产者1 (通配符模式)
	 *
	 * @param routingKey
	 * @param msg
	 */
	public void sendTopicTest1(String routingKey, String msg) {
		MessageProperties messageProperties = new MessageProperties();
		// 设置过期时间,单位:毫秒 TTL (所有发送过来的队列消息过期时间)
		messageProperties.setExpiration("20000");
		byte[] msgBytes = msg.getBytes();
		Message message = new Message(msgBytes, messageProperties);
		rabbitTemplate.convertAndSend(RabbitMQConfig.TOPIC_EXCHANGE_NAME, routingKey, message);
	}

 测试

取消发送队列2的消息,放开队列1的消息

	@GetMapping("/getTopic")
	public String sendTopic(){
		sender.sendTopicTest1("1213457107.@qq.com","topic模式1,发送给1213457107@qq.com消息");
		// sender.sendTopicTest2("sms.@qq.com","topic模式2,发送给sms.@qq.com消息");
		return "ok";
	}

 发送消息

消息没有被消费

 测试前

测试后

 20秒钟后过期

死信队列

配置类

注意:死信队列必须优先创建,否则会报找不到交换机的错误。

package com.hikktn.config;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;


/**
 * @ClassName RabbitMQConfig
 * @Description TODO
 * @Author lisonglin
 * @Date 2021/4/9 14:49
 * @Version 1.0
 */
@Configuration
public class RabbitMQConfig {

    // 路由模式 - 死信队列
	public static final String DEAD_EXCHANGE_NAME = "dead_exchange";
	public static final String LIVE_EXCHANGE_NAME = "live_exchange";
	private static final String DEAD_QUEUE_NAME_1 = "dead_queue_1";
	private static final String LIVE_QUEUE_NAME_2 = "live_queue_2";
	private static final String TOPIC_ROUTING_KEY_DEAD = "Dead";
	private static final String TOPIC_ROUTING_KEY_LIVE = "Live";

	// 死信队列
	// 死队列
	@Bean("dead_queue_1")
	public Queue createDeadDlxQueue1() {
		Map<String,Object> args = new HashMap<>(16);
		return QueueBuilder.durable(DEAD_QUEUE_NAME_1).withArguments(args).build();
	}


	// 死交换机
	@Bean("dead_exchange")
	public Exchange createDeadExchange() {
		return ExchangeBuilder.directExchange(DEAD_EXCHANGE_NAME).durable(true).build();
	}

	// 绑定死队列和死交换机
	@Bean
	public Binding bindingDeadQueueAndDlxExchange(@Qualifier("dead_queue_1") Queue queue,
	                                              @Qualifier("dead_exchange") Exchange exchange) {
		return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY_DEAD).noargs();
	}

	// 活队列
	@Bean("live_queue_2")
	public Queue createLiveDlxQueue2() {
		// 死信队列
		Map<String,Object> args = new HashMap<>(16);
		// x-message-ttl:设置队列的过期时间
		args.put("x-message-ttl",20000);
		// x-max-length:设置队列的长度限制
		args.put("x-max-length",20);
		// x-dead-letter-exchange:死信交换机名称
		args.put("x-dead-letter-exchange",DEAD_EXCHANGE_NAME);
		// x-dead-letter-routing-key:发送给死信交换机的routingkey
		args.put("x-dead-letter-routing-key", ROUTING_KEY_DEAD);
		return QueueBuilder.durable(LIVE_QUEUE_NAME_2).withArguments(args).build();
	}

	// 活交换机
	@Bean("live_exchange")
	public Exchange createLiveExchange() {
		return ExchangeBuilder.directExchange(LIVE_EXCHANGE_NAME).durable(true).build();
	}

	// 绑定活队列和活交换机
	@Bean
	public Binding bindingLiveQueueAndDlxExchange(@Qualifier("live_queue_2") Queue queue,
	                                           @Qualifier("live_exchange") Exchange exchange) {
		return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY_LIVE).noargs();
	}
}

 第一种方式:出现异常

消费者1

package com.hikktn.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @ClassName DirectListener1
 * @Description TODO
 * @Author lisonglin
 * @Date 2021/4/9 21:55
 * @Version 1.0
 */
@Component
public class LiveListener {

	@RabbitListener(queues = "live_queue_2")
	public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
		System.out.println("live_queue_2消费者1接收到的消息为:" + message);
		try {
			int i = 3 / 0;
			/**
			 * 无异常就确认消息
			 * basicAck(long deliveryTag, boolean multiple)
			 * deliveryTag:取出来当前消息在队列中的的索引;
			 * multiple:为true的话就是批量确认,如果当前deliveryTag为5,那么就会确认
			 * deliveryTag为5及其以下的消息;一般设置为false
			 */
			channel.basicAck(tag, false);
		}catch (Exception e){
			/**
			 * 有异常就绝收消息
			 * basicNack(long deliveryTag, boolean multiple, boolean requeue)
			 * requeue:true为将消息重返当前消息队列,还可以重新发送给消费者;
			 *         false:将消息丢弃
			 */
			channel.basicNack(tag,false,false);
		}
	}
}

消费者2

package com.hikktn.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @ClassName DirectListener1
 * @Description TODO
 * @Author lisonglin
 * @Date 2021/4/9 21:55
 * @Version 1.0
 */
@Component
public class DeadListener {

	@RabbitListener(queues = "dead_queue_1")
	public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
		System.out.println("dead_queue_1消费者1接收到的消息为:" + message);
		try {
			int i = 3 / 1;
			/**
			 * 无异常就确认消息
			 * basicAck(long deliveryTag, boolean multiple)
			 * deliveryTag:取出来当前消息在队列中的的索引;
			 * multiple:为true的话就是批量确认,如果当前deliveryTag为5,那么就会确认
			 * deliveryTag为5及其以下的消息;一般设置为false
			 */
			channel.basicAck(tag, false);
		}catch (Exception e){
			/**
			 * 有异常就绝收消息
			 * basicNack(long deliveryTag, boolean multiple, boolean requeue)
			 * requeue:true为将消息重返当前消息队列,还可以重新发送给消费者;
			 *         false:将消息丢弃
			 */
			channel.basicNack(tag,true,false);
		}
	}
}

 生产者

package com.hikktn.producer;

import com.hikktn.config.RabbitMQConfig;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @ClassName Sender
 * @Description 生产者
 * @Author lisonglin
 * @Date 2021/4/9 16:54
 * @Version 1.0
 */
@Component
public class Sender {
	@Autowired
	private RabbitTemplate rabbitTemplate;

	/**
	 * 生产者2 (通配符模式)
	 *
	 * @param routingKey
	 * @param msg
	 */
	public void sendDeadTopicTest(String routingKey, String msg) {
		rabbitTemplate.convertAndSend(RabbitMQConfig.LIVE_EXCHANGE_NAME, routingKey, msg);
	}

}

控制器

	@GetMapping("/getDead")
	public String sendDeadTopic(){
        // 发送的消息,必须key和exchange和活信一样
		sender.sendDeadTopicTest("Live","死信队列测试,发送给Dead消息");
		return "ok";
	}

 结果

 第二种方式:队列超时

测试

取消各种消费者

	// @RabbitListener(queues = "live_queue_2")
	public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
		System.out.println("live_queue_2消费者1接收到的消息为:" + message);
		try {
			// int i = 3 / 0;
			/**
			 * 无异常就确认消息
			 * basicAck(long deliveryTag, boolean multiple)
			 * deliveryTag:取出来当前消息在队列中的的索引;
			 * multiple:为true的话就是批量确认,如果当前deliveryTag为5,那么就会确认
			 * deliveryTag为5及其以下的消息;一般设置为false
			 */
			channel.basicAck(tag, false);
		}catch (Exception e){
			/**
			 * 有异常就绝收消息
			 * basicNack(long deliveryTag, boolean multiple, boolean requeue)
			 * requeue:true为将消息重返当前消息队列,还可以重新发送给消费者;
			 *         false:将消息丢弃
			 */
			channel.basicNack(tag,false,false);
		}
	}
	// @RabbitListener(queues = "dead_queue_1")
	public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
		System.out.println("dead_queue_1消费者1接收到的消息为:" + message);
		try {
			// int i = 3 / 1;
			/**
			 * 无异常就确认消息
			 * basicAck(long deliveryTag, boolean multiple)
			 * deliveryTag:取出来当前消息在队列中的的索引;
			 * multiple:为true的话就是批量确认,如果当前deliveryTag为5,那么就会确认
			 * deliveryTag为5及其以下的消息;一般设置为false
			 */
			channel.basicAck(tag, false);
		}catch (Exception e){
			/**
			 * 有异常就绝收消息
			 * basicNack(long deliveryTag, boolean multiple, boolean requeue)
			 * requeue:true为将消息重返当前消息队列,还可以重新发送给消费者;
			 *         false:将消息丢弃
			 */
			channel.basicNack(tag,true,false);
		}
	}

 测试前

 结果

 20秒钟后

第三种方式:队列超过限制长度

测试

	/**
	 * 生产者2 (通配符模式)
	 *
	 * @param routingKey
	 * @param msg
	 */
	public void sendDeadTopicTest(String routingKey, String msg) {
		for (int i = 0; i < 30; i++) {
			rabbitTemplate.convertAndSend(RabbitMQConfig.LIVE_EXCHANGE_NAME, routingKey, msg);
		}
	}

超过队列的限制 

 20秒钟后过期

延迟队列

就是利用DLX+TTL技术实现,上面的死信队列,只需要注释掉活队列的消费者,而后利用活队列的过期时间,自动将活队列的消息变为死队列。

具体看代码

package com.hikktn.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @ClassName DirectListener1
 * @Description TODO
 * @Author lisonglin
 * @Date 2021/4/9 21:55
 * @Version 1.0
 */
@Component
public class LiveListener {

	// @RabbitListener(queues = "live_queue_2")
	public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
		System.out.println("live_queue_2消费者1接收到的消息为:" + message);
		try {
			// int i = 3 / 0;
			/**
			 * 无异常就确认消息
			 * basicAck(long deliveryTag, boolean multiple)
			 * deliveryTag:取出来当前消息在队列中的的索引;
			 * multiple:为true的话就是批量确认,如果当前deliveryTag为5,那么就会确认
			 * deliveryTag为5及其以下的消息;一般设置为false
			 */
			channel.basicAck(tag, false);
		}catch (Exception e){
			/**
			 * 有异常就绝收消息
			 * basicNack(long deliveryTag, boolean multiple, boolean requeue)
			 * requeue:true为将消息重返当前消息队列,还可以重新发送给消费者;
			 *         false:将消息丢弃
			 */
			channel.basicNack(tag,false,false);
		}
	}
}

测试效果 如下:

以上,完结!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hikktn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值