RabbitMQ高级特性(二)
一、
-
-
首先需要开启confirm模式
-
消息从producer到达exchange后,会执行一个confirmCallback回调函数
-
该回调函数的方法中有个ack参数
-
ack = true,则发送成功
-
ack = false,则发送失败
-
-
-
return模式:
-
首先需要开启return模式
-
消息从exchange路由到queue后
-
如果投递成功,不会执行一个returnCallback回调函数
-
二、
消费端接收到消息后有三种ack方式:
-
不确认:ack = "none"
-
手动确认:ack = "manual"
-
自动确认:ack = "auto"
自动确认是指,消息一旦被consumer接收到则自动确认收到,并将相应的message从RabbitMQ的消息缓存中移除。但是在实际的业务处理中,很可能是消息被接收到了,但是业务处理出现了异常,那么消息从缓存中移除即该消息就被丢弃了。如果设置了手动确认,则需要在业务处理成功后,调用channel.basicAck()方法手动签收,如果出现了异常,则调用channel.basicNack()方法,让其自动重发消息。
三、
RabbitMQ的限流操作:只需要在消费方配置每次拉取的消息数量。

四、
延时队列,即消息进入队列后不会被立即消费,只有到达指定的时间后才会被消费。比如,购买火车票:30分内完成支付,30分后,若不支付,则取消订单。比较遗憾的是,RabbitMQ本身没有直接支持延迟队列的功能,但是可以通过 TTL + DLX 组合来实现延时队列的效果。
4.1、TTL
-
-
当消息到达存活时间后,该消息还没有被消费,会自动被清除
-
RabbitMQ可以对消息设置过期时间也可以对整个队列设置过期时间
-
如果都设置了,哪个时间先到则生效
-
-
环境搭建:
1、
<!--起步依赖--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> </dependencies>
2、编写启动类
@SpringBootApplication public class DelayApplication { public static void main(String[] args) { SpringApplication.run(DelayApplication.class, args); } }
3、编写application.yml文件
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
代码实现 TTL:
1、
@Configuration public class TTLConfig { // 创建队列 @Bean public Queue ttlQueue(){ // 创建队列,并且指定队列的过期时间 return QueueBuilder.durable("ttl-queue").withArgument("x-message-ttl", 10000).build(); } @Bean public Exchange ttlExchange(){ // 注意,由于routingkey我们使用了匹配,因此我们要创建topic类型的交换机 return new TopicExchange("ttl-exchange", true, false); } // 队列绑定到交换机 @Bean public Binding queueBindToExchangeByTTL(Queue ttlQueue, Exchange ttlExchange){ return BindingBuilder.bind(ttlQueue).to(ttlExchange).with("ttl.#").noargs(); } }
@Test public void testTTL(){ // 可以设置消息的属性消息 MessagePostProcessor messagePostProcessor = new MessagePostProcessor() { @Override public Message postProcessMessage(Message message) throws AmqpException { // 设置消息的过期时间 5s message.getMessageProperties().setExpiration("5000"); return message; } }; rabbitTemplate.convertAndSend("test-ttl-exchange","ttl.hehe", "ttl消息", messagePostProcessor); }
4.2 、DLX
4.2.1 概念
DLX:Dead-Letter-Exchange,死信交换机。当消息成为Dead Message后,可以被重新发送到另一个交换机,这个交换机就称为死信交换机。
1、生成者将消息发送到交换机后,由交换机路由到指定的队列
-
-
队列消息长度达到限制
-
消费者拒签消息
-
原队列中存在消息过期设置,消息到达超时时间未被消费
-
3、DLX再将这个消息路由给死信队列,并且由对应的消费者消费

4.2.1 代码实现
创建配置类:
@Configuration public class DLXConfig { // 创建交换机 @Bean public Exchange delayExchange(){ return new DirectExchange("delay-exchange"); } // 创建队列 @Bean public Queue delayQueue(){ Map<String, Object> args = new HashMap<>(); args.put("x-message-ttl", 20000); // 队列过期时间 args.put("x-max-length", 10000000); // 队列中消息数量 args.put("x-dead-letter-exchange", "dlx-exchange"); // 绑定死信交换机 args.put("x-dead-letter-routing-key", "dlx-routing-key"); // 绑定死信路由器 return QueueBuilder.durable("delay-queue").withArguments(args).build(); } // 将队列绑定到交换机 @Bean public Binding delayBinding(Queue delayQueue, Exchange delayExchange){ return BindingBuilder.bind(delayQueue).to(delayExchange).with("delay-routing-key").noargs(); } // 创建死信交换机 @Bean public Exchange dlxExhange(){ return new DirectExchange("dlx-exchange"); } // 创建死信队列 @Bean public Queue dlxQueue(){ return new Queue("dlx-queue"); } // 将死信队列绑定到死信交换机上 @Bean public Binding dlxBinding(Queue dlxQueue, Exchange dlxExhange){ return BindingBuilder.bind(dlxQueue).to(dlxExhange).with("dlx-routing-key").noargs(); } }
单元测试:
@Test public void testDLX(){ // 发送消息 rabbitTemplate.convertAndSend("delay-exchange","delay-routing-key", "死信消息"); }
发送消息:

20s后再看:

4.3 代码实现延时队列
前面介绍中说过,通过 TTL + DLX 组合来实现延时队列的效果。在 4.2 章节中其实已经实现了延时队列了,我们只需要去监听死信队列去消费消息即可。
创建监听器,如下:
@Component @RabbitListener(queues = {"dlx-queue"}) public class DelayListener { @RabbitHandler public void readMsg(String msg){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); System.out.println("消费消息时间:" + sdf.format(new Date())); System.out.println("获取到的消息为:" + msg); } }
单元测试:
@Test public void testDLX(){ // 发送消息 rabbitTemplate.convertAndSend("delay-exchange","delay-routing-key", "死信消息"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); System.out.println("发送消息时间:" + sdf.format(new Date())); }


浙公网安备 33010602011771号