@[toc]
RabbitMQ 中的音讯长期未被生产会过期吗?用过 RabbitMQ 的小伙伴可能都有这样的疑难,明天松哥就来和大家捋一捋这个问题。
1. 默认状况
首先咱们来看看默认状况。
默认状况下,音讯是不会过期的,也就是咱们素日里在音讯发送时,如果不设置任何音讯过期的相干参数,那么音讯是不会过期的,即便音讯没被生产掉,也会始终存储在队列中。
这种状况具体代码就不必我再演示了吧,松哥之前的文章但凡波及到 RabbitMQ 的,基本上都是这样的。
2. TTL
TTL(Time-To-Live),音讯存活的工夫,即音讯的有效期。如果咱们心愿音讯可能有一个存活工夫,那么咱们能够通过设置 TTL 来实现这一需要。如果音讯的存活工夫超过了 TTL 并且还没有被音讯,此时音讯就会变成 死信
,对于 死信
以及 死信队列
,松哥前面再和大家介绍。
TTL 的设置有两种不同的形式:
- 在申明队列的时候,咱们能够在队列属性中设置音讯的有效期,这样所有进入该队列的音讯都会有一个雷同的有效期。
- 在发送音讯的时候设置音讯的有效期,这样不同的音讯就具备不同的有效期。
那如果两个都设置了呢?
以工夫短的为准。
当咱们设置了音讯有效期后,音讯过期了就会被从队列中删除了(进入到死信队列,后文一样,不再标注),然而两种形式对应的删除机会有一些差别:
- 对于第一种形式,当音讯队列设置过期工夫的时候,那么音讯过期了就会被删除,因为音讯进入 RabbitMQ 后是存在一个音讯队列中,队列的头部是最早要过期的音讯,所以 RabbitMQ 只须要一个定时工作,从头部开始扫描是否有过期音讯,有的话就间接删除。
- 对于第二种形式,当音讯过期后并不会立马被删除,而是当音讯要投递给消费者的时候才会去删除,因为第二种形式,每条音讯的过期工夫都不一样,想要晓得哪条音讯过期,必须要遍历队列中的所有音讯能力实现,当音讯比拟多时这样就比拟消耗性能,因而对于第二种形式,当音讯要投递给消费者的时候才去删除。
介绍完 TTL 之后,接下来咱们来看看具体用法。
接下来所有代码松哥都以 Spring Boot 中封装的 AMPQ 为例来解说。
2.1 单条音讯过期
咱们先来看单条音讯的过期工夫。
首先创立一个 Spring Boot 我的项目,引入 Web 和 RabbitMQ 依赖,如下:
而后在 application.properties 中配置一下 RabbitMQ 的连贯信息,如下:
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
接下来略微配置一下音讯队列:
@Configuration
public class QueueConfig {
public static final String JAVABOY_QUEUE_DEMO = "javaboy_queue_demo";
public static final String JAVABOY_EXCHANGE_DEMO = "javaboy_exchange_demo";
public static final String HELLO_ROUTING_KEY = "hello_routing_key";
@Bean
Queue queue() {return new Queue(JAVABOY_QUEUE_DEMO, true, false, false);
}
@Bean
DirectExchange directExchange() {return new DirectExchange(JAVABOY_EXCHANGE_DEMO, true, false);
}
@Bean
Binding binding() {return BindingBuilder.bind(queue())
.to(directExchange())
.with(HELLO_ROUTING_KEY);
}
}
这个配置类次要干了三件事:配置音讯队列、配置交换机以及将两者绑定在一起。
- 首先配置一个音讯队列,new 一个 Queue:第一个参数是音讯队列的名字;第二个参数示意音讯是否长久化;第三个参数示意音讯队列是否排他,个别咱们都是设置为 false,即不排他;第四个参数示意如果该队列没有任何订阅的消费者的话,该队列会被主动删除,个别实用于长期队列。
- 配置一个 DirectExchange 交换机。
- 将交换机和队列绑定到一起。
这段配置应该很简略,没啥好解释的,有一个排他性,松哥这里略微多说两句:
对于排他性,如果设置为 true,则该音讯队列只有创立它的 Connection 能力拜访,其余的 Connection 都不能拜访该音讯队列,如果试图在不同的连贯中从新申明或者拜访排他性队列,那么零碎会报一个资源被锁定的谬误。另一方面,对于排他性队列而言,当连贯断掉的时候,该音讯队列也会主动删除(无论该队列是否被申明为持久性队列都会被删除)。
接下来提供一个音讯发送接口,如下:
@RestController
public class HelloController {
@Autowired
RabbitTemplate rabbitTemplate;
@GetMapping("/hello")
public void hello() {Message message = MessageBuilder.withBody("hello javaboy".getBytes())
.setExpiration("10000")
.build();
rabbitTemplate.convertAndSend(QueueConfig.JAVABOY_QUEUE_DEMO, message);
}
}
在创立 Message 对象的时候咱们能够设置音讯的过期工夫,这里设置音讯的过期工夫为 10 秒。
这就能够啦!
接下来咱们启动我的项目,进行音讯发送测试。当音讯发送胜利之后,因为没有消费者,所以这条音讯并不会被生产。关上 RabbitMQ 治理页面,点击到 Queues 选项卡,10s 之后,咱们会发现音讯曾经不见了:
很简略吧!
单条音讯设置过期工夫,就是在音讯发送的时候设置一下音讯有效期即可。
2.2 队列音讯过期
给队列设置音讯过期工夫,形式如下:
@Bean
Queue queue() {Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 10000);
return new Queue(JAVABOY_QUEUE_DEMO, true, false, false, args);
}
设置实现后,咱们批改音讯的发送逻辑,如下:
@RestController
public class HelloController {
@Autowired
RabbitTemplate rabbitTemplate;
@GetMapping("/hello")
public void hello() {Message message = MessageBuilder.withBody("hello javaboy".getBytes())
.build();
rabbitTemplate.convertAndSend(QueueConfig.JAVABOY_QUEUE_DEMO, message);
}
}
能够看到,音讯失常发送即可,不必设置音讯过期工夫。
OK,启动我的项目,发送一条音讯进行测试。查看 RabbitMQ 治理页面,如下:
能够看到,音讯队列的 Features 属性为 D 和 TTL,D 示意音讯队列中音讯长久化,TTL 则示意音讯会过期。
10s 之后刷新页面,发现音讯数量曾经复原为 0。
这就是给音讯队列设置音讯过期工夫,一旦设置了,所有进入到该队列的音讯都有一个过期工夫了。
2.3 非凡状况
还有一种非凡状况,就是将音讯的过期工夫 TTL 设置为 0,这示意如果音讯不能立马生产则会被立刻丢掉,这个个性能够局部代替 RabbitMQ3.0 以前反对的 immediate 参数,之所以所局部代替,是因为 immediate 参数在投递失败会有 basic.return 办法将音讯体返回(这个性能能够利用死信队列来实现)。
具体代码松哥就不演示了,这个应该比拟容易。
3. 死信队列
有小伙伴不禁要问,被删除的音讯去哪了?真的被删除了吗?非也非也!这就波及到死信队列了,接下来咱们来看看死信队列。
3.1 死信交换机
死信交换机,Dead-Letter-Exchange 即 DLX。
死信交换机用来接管死信音讯(Dead Message)的,那什么是死信音讯呢?个别音讯变成死信音讯有如下几种状况:
- 音讯被回绝(Basic.Reject/Basic.Nack),井且设置 requeue 参数为 false
- 音讯过期
- 队列达到最大长度
当音讯在一个队列中变成了死信音讯后,此时就会被发送到 DLX,绑定 DLX 的音讯队列则称为死信队列。
DLX 实质上也是一个普普通通的交换机,咱们能够为任意队列指定 DLX,当该队列中存在死信时,RabbitMQ 就会主动的将这个死信公布到 DLX 下来,进而被路由到另一个绑定了 DLX 的队列上(即死信队列)。
3.2 死信队列
这个好了解,绑定了死信交换机的队列就是死信队列。
3.3 实际
咱们来看一个简略的例子。
首先咱们来创立一个死信交换机,接着创立一个死信队列,再将死信交换机和死信队列绑定到一起:
public static final String DLX_EXCHANGE_NAME = "dlx_exchange_name";
public static final String DLX_QUEUE_NAME = "dlx_queue_name";
public static final String DLX_ROUTING_KEY = "dlx_routing_key";
/**
* 配置死信交换机
*
* @return
*/
@Bean
DirectExchange dlxDirectExchange() {return new DirectExchange(DLX_EXCHANGE_NAME, true, false);
}
/**
* 配置死信队列
* @return
*/
@Bean
Queue dlxQueue() {return new Queue(DLX_QUEUE_NAME);
}
/**
* 绑定死信队列和死信交换机
* @return
*/
@Bean
Binding dlxBinding() {return BindingBuilder.bind(dlxQueue())
.to(dlxDirectExchange())
.with(DLX_ROUTING_KEY);
}
这其实跟一般的交换机,一般的音讯队列没啥两样。
接下来为音讯队列配置死信交换机,如下:
@Bean
Queue queue() {Map<String, Object> args = new HashMap<>();
// 设置音讯过期工夫
args.put("x-message-ttl", 0);
// 设置死信交换机
args.put("x-dead-letter-exchange", DLX_EXCHANGE_NAME);
// 设置死信 routing_key
args.put("x-dead-letter-routing-key", DLX_ROUTING_KEY);
return new Queue(JAVABOY_QUEUE_DEMO, true, false, false, args);
}
就两个参数:
- x-dead-letter-exchange:配置死信交换机。
- x-dead-letter-routing-key:配置死信
routing_key
。
这就配置好了。
未来发送到这个音讯队列上的音讯,如果产生了 nack、reject 或者过期等问题,就会被发送到 DLX 上,进而进入到与 DLX 绑定的音讯队列上。
死信音讯队列的生产和一般音讯队列的生产并无二致:
@RabbitListener(queues = QueueConfig.DLX_QUEUE_NAME)
public void dlxHandle(String msg) {System.out.println("dlx msg =" + msg);
}
很容易吧~
4. 小结
好啦,明天就和小伙伴们聊一聊 RabbitMQ 中的音讯过期问题,感兴趣的小伙伴能够去试试哦~
公众号江南一点雨后盾回复本文题目,能够获取本文案例下载链接。
参考资料:
- blog.csdn.net/u012988901/article/details/88958654