1.定义
MQ的是(Message Queue)的简写,本质是一个队列,FIFO先进先出,只不过RabbitMQ中的内容都是message。
1.1.作用
不同进程线程之间的信息通信。
MQ框架非常之多,比较流行的有RabbitMq、ActiveMq、ZeroMq、kafka,以及阿里开源的RocketMQ。
2.RabbitMQ
RabbitMQ是开源的基于AMQP基础上完整的,可复用的企业消息系统。支持主流的操作系统,Linux,windows,MacOS等.
2.1AMQP
AMQP是消息队列的一个协议。
2.2官网
地址:RabbitMQ Tutorials | RabbitMQ
2.3五种消息模型
虽然官网上显示的是六种消息模型,但其实第六种RPC并不是MQ,因此本文不详细讲解。所以现在就剩下了五种消息模型,在这五种之中其中3、4、5都属于订阅模型,只不过进入路由的方式不同。
2.3.1基本消息模型
RabbitMQ 是一个消息代理:它接受并转发消息。 您可以将其视为邮局:当您将要投递的邮件放入邮箱时,您可以确定邮递员最终会将邮件递送给收件人。 在这个类比中,RabbitMQ 是一个邮箱、一个邮局和一个信递员。 RabbitMQ 和邮局之间的主要区别在于它不处理纸张,而是接受、存储和转发二进制数据块 - 消息。--这段解释来自的RabbitMQ官网
在RabbitMQ中分为生产者 P(producer/ publisher),消费者C(consumer)
P(producer/ publisher):生产者,发送消息到RabbitMQ的一个应用程序。
C(consumer):消费者,消费和接收有类似的意思,消费者是一个主要用来等待接收消息的用户应用程序。
图中的hello就是生产者P发送的消息(邮件)到达了RabbitMQ也就是邮局中,然后RabbitMQ将message投递到了相应的消费者C中。
rabbitmq内部类似于邮箱的一个概念。虽然消息流经rabbitmq和你的应用程序,但是它们只能存储在队列中。队列只受主机的内存和磁盘限制,实质上是一个大的消息缓冲区。许多生产者可以发送消息到一个队列,许多消费者可以尝试从一个队列接收数据。
总之:生产者将消息发送到队列,消费者从队列中获取消息,队列是存储消息的缓冲区。
2.3.2work消息模型
工作队列或者竞争消费者模式
工作队列,又称任务队列。主要思想就是避免执行资源密集型任务时,必须等待它执行完成。相反我们稍后完成任务,我们将任务封装为消息并将其发送到队列。 在后台运行的工作进程将获取任务并最终执行作业。当你运行许多工人时,任务将在他们之间共享,但是一个消息只能被一个消费者获取。
总之:让多个消费者绑定到一个队列,共同消费队列中的消息。队列中的消息一旦消
费,就会消失,因此任务是不会被重复执行的。
2.3.3订阅模型
解释:
1.1个生产者对应多个消费者。
2.每个消费者都有一个自己的队列
3.生产者产生消息后不是直接存放到队列中的,而是将消息发送到交换机中,再有交换机发送到各个队列中去。
4.每一个队列都与交换机进行绑定。
X(Exchanges):交换机一方面:接收生产者发送的消息。另一方面:知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。
Exchanges交换机有以下几种类型:
1.Publish/Subscribe:一次向多个消费者发送消息。
2.Routing:有选择地接收消息。
3. Topics:根据模式主题(router key)接收消息。
交换机只具有转发消息的功能不能够存储消息,如果没有任何的队列Queue与交换机进行绑定,那么消息将会消失。
2.3.3.1订阅模型 Publish/Subscribe/Fanout
- 每一个消费者都对应一个队列
- 每个队列都与交换机进行绑定
- 生产者将message传送到交换机中,交换机来决定要将message传递给哪个队列,生产者无法决定。
- fanout模型下交换机将message传递给所有与当前交换机绑定的队列,与交换机进行绑定的队列都会收到消息,这样就实现了发送一次消息被多个消费者接受到。
2.3.3.2订阅模型Routing/Direct
在Direct模型下,队列与交换机的绑定,不能是任意绑定了,而是要指定一个RoutingKey(路由key),消息的发送方在向Exchange发送消息时,也必须指定消息的routing key。
P:生产者,向Exchange发送消息,发送消息时,会指定一个routing key。
X:Exchange(交换机),接收生产者的消息,然后把消息递交给 与routing key完全匹配的队列
C1:消费者,其所在队列指定了需要routing key 为 orange的消息
C2:消费者,其所在队列指定了需要routing key 为 black、green 的消息
2.3.3.3订阅模型 topics
Topic 类型的 Exchange 与 Direct 相比,都是可以根据 RoutingKey 把消息路由到不同的队列。只不过 Topic 类型 Exchange 可以让队列在绑定 Routing key 的时候使用通配符!
通配符规则:#:匹配一个或多个词*:匹配不多不少恰好 1 个词
2.3.4消费模型的代码示例
2.3.4.1.Springboot集成RabbitMQ,所以我们在SpringBoot中使用RabbitMQ非常的简单,只需要配置pom文件导入对spring-boot-starter-amqp的支持
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
1.配置application.yml文件
配置rabbitmq的安装地址、端口以及账户信息
spring:
rabbitmq:
host: 127.0.0.1
password: 123321
username: itcast
port: 5672
2.配置队列
@Configuration
public class RabbitConfig{
// 创建一个名为q_hello的队列
@Bean
public Queue queue() {
return new Queue("q_hello");
}
}
3.生产者
@Component
public class HelloSender {
@Autowired
private RabbitTemplate rabbitTemplate;
public void send() {
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//24小时制
String context = "hello " + date;
//简单对列的情况下routingKey即为Q名
this.rabbitTemplate.convertAndSend("q_hello", context);
}
}
4.消费者
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "q_hello")
public class RabbitMQListener {
@RabbitHandler
public void process(String message) {
System.out.println("Receiver : " + message);
}
}
5.测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringAmqpTest {
@Autowired
private HelloSender helloSender;
@Test
public void testRabbitMQSimple() {
helloSender.send();
}
}
2.3.4.2 (Work模式)
注册两个Receiver
@Component
@RabbitListener(queues = "q_hello")
public class RabbitMQListener2 {
@RabbitHandler
public void process(String message) {
System.out.println("Receiver2 : " + message);
}
}
生产者
public void send(int i) {
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//24小时制
String context = "hello " + i + " " + date;
//简单对列的情况下routingKey即为Q名
this.rabbitTemplate.convertAndSend("q_hello", context);
}
测试
@Test
public void oneToMany() throws Exception {
for (int i=0;i<100;i++){
helloSender.send(i);
Thread.sleep(300);
}
}
2.3.4.3 (Fanout模式)
创建两个队列,一个交换机,并将两个队列与交换机进行绑定。
@Configuration
public class FanoutConfig {
@Bean
public Queue queue() {
return new Queue("q_hello");
}
@Bean
public Queue queue1() {
return new Queue("q_hello1");
}
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("mq.fanout");
}
@Bean
public Binding fanoutBindingQueue(Queue queue, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(queue).to(fanoutExchange);
}
@Bean
public Binding fanoutBindingQueue(Queue queue1, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(queue1).to(fanoutExchange);
}
}
创建两个消费者
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "q_hello")
public class Receiver {
@RabbitHandler
public void process(String hello) {
System.out.println("q_hello: " + hello + "/n");
}
}
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "q_hello1")
public class Receiver1 {
@RabbitHandler
public void process(String hello) {
System.out.println("q_hello1: " + hello + "/n");
}
}
生产者
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringAmqpTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testFanoutMessage() {
String message = "hello fanout ExChange";
//convertAndSend方法的第一个形参是exchange的名称,第二个是发送的message内容
rabbitTemplate.convertAndSend("mq.fanout",message);
}
}
2.3.4.4 (Direct模式)
这里我们采用一种新的创建队列与交换机的方式
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue1"),
exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
key = {"red","blue"}
))
public void listenerDirectQueue1(String msg) {
System.out.println("消费者接收listenerDirectQueue1消息" + "[" + msg +"]");
}
// directExchange发布订阅
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue2"),
exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
key = {"red","yellow"}
))
public void listenerDirectQueue2(String msg) {
System.out.println("消费者接收listenerDirectQueue2消息" + "[" + msg +"]");
}
exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT)type默认就是ExchangeTypes.DIRECT,所以可写可不写。
生产者
@Test
public void testSendDirectExchange() {
String exchangeName = "itcast.direct";
String message = "hello every one direct";
// convertAndSend有三个形参,第一个形参是交换机名称,第二个是router key作用是指定你需要发送到哪一个队列中,第三个是Message。
// 这里的router key 是yellow所以只有listenerDirectQueue2可以接收到
rabbitTemplate.convertAndSend(exchangeName,"yellow",message);
}
2.3.4.5 (Topic模式)
@Component
public class SpringRabbitListener {
// topicExchange
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "topic.queue1"),
exchange = @Exchange(name = "topicExchange",type = ExchangeTypes.TOPIC),
key = "china.#"
))
public void listenerTopicQueue1(String msg) {
System.out.println("listenerTopicQueue1" + "[" + msg +"]");
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "topic.queue2"),
exchange = @Exchange(name = "topicExchange",type = ExchangeTypes.TOPIC),
key = "#.news"
))
public void listenerTopicQueue2(String msg) {
System.out.println("listenerTopicQueue2" + "[" + msg +"]");
}
}
@Test
public void testSendTopicExchange() {
String exchangeName = "topicExchange";
String message = "hello every one direct hahahah";
//router key 是china.news 所以上述两个都可以接收到
rabbitTemplate.convertAndSend(exchangeName,"china.news",message);
}
2.4 消息转换器
- 默认的 SimpleMessageConverter 在发送消息时会将对象序列化成字节数组,若要反序列化对象,需要自定义 MessageConverter
@Configuration
public class RabbitMQConfig {
@Bean
public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory){
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(new MessageConverter() {
@Override
public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
return null;
}
@Override
public Object fromMessage(Message message) throws MessageConversionException {
try(ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(message.getBody()))){
return (User)ois.readObject();
}catch (Exception e){
e.printStackTrace();
return null;
}
}
});
return factory;
}
}
@Component
@RabbitListener(queues = "consumer_queue")
public class Receiver {
@RabbitHandler
public void processMessage1(User user) {
System.out.println(user.getName());
}
}
2.4.1 使用 JSON 序列化与反序列化
- RabbitMQ 提供了 Jackson2JsonMessageConverter 来支持消息内容 JSON 序列化与反序列化
- 消息发送者在发送消息时应设置 MessageConverter 为 Jackson2JsonMessageConverter
配置消息转换器
- 引入依赖
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.10</version>
</dependency>
- 在publisher服务声明MessageConverter:
@SpringBootApplication
public class PublisherApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
- 发送消息
User user = new User("name",20);
rabbitTemplate.convertAndSend("topic.exchange","key.1",user);
2.5 MabbitMQ中注解
2.5.1 @Payload和@Headers注解
@Component
public class MessageHandler {
//获取消息的头属性和body属性
@RabbitListener(queues = "zhihao.miao.order")
public void handleMessage(@Payload String body, @Headers Map<String,Object> headers){
System.out.println("====消费消息===handleMessage");
System.out.println(headers);
System.out.println(body);
}
}
获取单一个Header的属性,Header还有其他的一些属性,比如required
,defaultvalue
等属性,顾名思义:
@Component
public class MessageHandler {
//获取特定的消息
@RabbitListener(queues = "zhihao.miao.order")
public void handleMessage(@Payload String body,@Header String token){
System.out.println("====消费消息===handleMessage");
System.out.println(token);
System.out.println(body);
}
}
2.5.2 @RabbitListener 与 @RabbitHandler
@RabbitListener
@RabbitListener用于注册Listener时使用的信息:如queue,exchange,key、ListenerContainerFactory和RabbitAdmin的bean name。
@RabbitListener(containerFactory = "rabbitListenerContainerFactory", bindings = @QueueBinding(
value = @Queue(value = "${mq.config.queue}", durable = "true"),
exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.TOPIC),
key = "${mq.config.key}"), admin = "rabbitAdmin")
扫描到bean带有该注解后,首先会将注解的内容封装到Endpoint对象中并和ListenerContainerFactory的实例一起添加到上面的RabbitListenerEndpointRegistry实例中。添加的时候会创建相应的ListenerContainer实例并添加Listener对象。
@RabbitHandler
@RabbitListener 和 @RabbitHandler结合使用,不同类型的消息使用不同的方法来处理。
public class CommandListener{
@RabbitHandler
public void handler1(ApiMessage msg){
System.out.println(msg);
}
@RabbitHandler
public void handler2(Map msg){
System.out.println(msg);
}
}
3. RabbitMQ的消息确认机制
消息从生产者发送到exchange,再到queue,再到消费者的过程中会有消息丢失的可能性,例如:
1.发送时丢失消息:
- 生产者发送的消息未传递到exchange
- 消息到达exchange后没有传递给queue
2.MQ宕机,queue将消息丢失
3.消费者接受到消息为消费就宕机
RabbitMQ消息确认机制指的是在消息传递过程中,发送方发送消息后,接收方需要对消息进行确认,以确保消息被正确地接收和处理。RabbitMQ的消息确认机制分为两种:
生产者确认机制:生产者发送消息后,需要等待RabbitMQ服务器的确认消息,以确保消息已经被成功地发送到RabbitMQ服务器。如果RabbitMQ服务器没有收到消息或者消息发送失败,生产者会收到一个确认消息,从而可以进行重发或者其他处理。
消费者确认机制:消费者接收到消息后,需要向RabbitMQ服务器发送确认消息,以告诉服务器已经成功地接收并处理了该消息。如果消费者没有发送确认消息,RabbitMQ服务器会认为该消息没有被正确地处理,从而会将该消息重新发送给其他消费者进行处理。
代码演示
注意: 确认机制发送消息时,需要给每一个消息设置一个全局唯一的id,以区分不同的消息,避免ack冲突。
ReturnCallback:指的是消息传递到了exchange,但是路由的过程中失败;
ConfirmCallback:指的是消息传递到exchange就失败;
3.1 生产者确认:
1. 在publisher的application.yml中添加好配置
spring:
rabbitmq:
publisher-confirm-type: correlated
# 开启publisher-confirm,这里支持两种类型:simple(同步等待confirm结果,直至超时)、correlated(异步回调,定义ConfirmCallback,MQ返回结果时回调这个ConfirmCallback)
publisher-returns: true
# 开启publish-return功能,同样基于callback机制,不过定义ReturnCallback
template:
mandatory: true
# 定义消息路由失败时的策略,true,则调用ReturnCallback;false:则直接丢弃掉
2. 每一个RabbitTemplate只能配置一个ReturnCallback,因此需要在项目启动过程中配置
@Slf4j
@Configuration
public class CommonConfig implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 获取RabbitTemplate对象
RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
// 配置ReturnCallback
rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
log.error("消息发送到队列失败,响应码:{}, 失败原因:{}, 交换机: {}, 路由key:{}, 消息: {}",
replyCode, replyText, exchange, routingKey, message.toString());
// 如果有需要的话,重发消息
});
}
}
3. 发送消息,指定消息ID,消息ConfirmCallback
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringAmqpTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSendMessage2SimpleQueue() throws InterruptedException {
// 1.准备消息
String message = "hello, spring amqp!";
// 2.准备CorrelationData
// 2.1.消息ID
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
// 2.2.准备ConfirmCallback
correlationData.getFuture().addCallback(result -> {
// 判断结果
if (result.isAck()) {
// ACK
log.debug("消息成功投递到交换机!消息ID: {}", correlationData.getId());
} else {
// NACK
log.error("消息投递到交换机失败!消息ID:{}", correlationData.getId());
// 重发消息
}
}, ex -> {
// 记录日志
log.error("消息发送失败!", ex);
// 重发消息
});
// 3.发送消息
rabbitTemplate.convertAndSend("amq.topic", "a.simple.test", message, correlationData);
}
}
3.2 消息持久化
MQ默认是内存存储消息,开启持久化功能可以确保缓存在MQ中的消息不丢失。
1. 交换机持久化
@Configuration
public class CommonConfig {
@Bean
public DirectExchange simpleDirect(){
// 三个参数: 交换机名称、是否持久化、当没有queue与其绑定时是否自动删除
return new DirectExchange("simple.direct",true,false);
}
}
2. 队列持久化
@Bean
public Queue simpleQueue(){
// 使用QueueBuilder构建队列,durable就是持久化 return new Queue("simple.queue",true)也可使队列持久化
return QueueBuilder.durable("simple.queue").build();
}
3. 消息持久化
@Test
public void testDurableMessage() {
// 1.准备消息
Message message = MessageBuilder.withBody("hello, spring".getBytes(StandardCharsets.UTF_8)) // 消息体
.setDeliveryMode(MessageDeliveryMode.PERSISTENT) // 持久化
.build();
// 2.发送消息
rabbitTemplate.convertAndSend("simple.queue", message);
}
3.3 消费者确认
RabbitMQ支持消费者确认机制,消费者处理消息后可以向MQ发送ACK回执,MQ收到ACK回执后才会删除该消息。
SpringAMQP允许三种确认模式:
- manual:手动ack,需要在业务代码结束后调用api发送ack。
- auto:自动ack,又spring检测listener的代码是否出现异常,如果没有异常返回ack否则返回nack。
- none:关闭ack,MQ鉴定消费者获取消息后会成功处理,因此消息投递后会被立即删除。
spring:
rabbitmq:
listener:
simple:
prefetch: 1
acknowledge-mode: auto # none, manual, auto三种模式对应上述
当消费者出现异常后,消息会不断地重新入队,然后再次发送给消费者,然后再次异常,然后再次入队,循环往复,导致MQ的消息处理飙升,带来不必要的压力。为了解决可以使用Spring的retry机制,消费者出现异常时利用本地重试,而不是无限制的入队。
spring:
rabbitmq:
listener:
simple:
prefetch: 1
acknowledge-mode: auto
retry:
enabled: true # 开启消费者失败重试
initial-interval: 1000 # 初始失败等待时长为1s
multiplier: 3 # 下次失败等待市场的倍数
max-attempts: 4 # 最大重试次数
开启重试模式后,重试次数耗尽,如果消息依旧失败,则需要有MessageRecover接口来处理,包含了三种不同的实现:
- RejectAndDontRequeueRecoverer:充实耗尽后,直接reject,丢弃消息,默认是这种方式。
- ImmediateRequeueMessageRecoverer:重试耗尽后,返回nack,重新入队。
- RepublishMessageRecoverer:重试耗尽后,将失败消息投递到指定的交换机中。
RepublishMessageRecoverer实现实例:
首先定义接受失败消息的交换机、队列
@Configuration
public class ErrorMessageConfig {
@Bean
public DirectExchange errorMessageExchange(){
return new DirectExchange("error.direct");
}
@Bean
public Queue errorQueue(){
return new Queue("error.queue");
}
@Bean
public Binding errorMessageBinding(){
return BindingBuilder.bind(errorQueue()).to(errorMessageExchange()).with("error");
}
}
然后在ErrorMessageConfig中定义RepublishMessageRecoverer:
@Bean
public MessageRecoverer republishMessageRecoverer(RabbitTemplate rabbitTemplate){
return new RepublishMessageRecoverer(rabbitTemplate, "error.direct", "error");
}
4. 死信交换机
当一个队列中满足下列情况之一时,就可以成为死信:
- 消费者使用basic.reject或者basic.nack声明消费失败,并且消息的requeue参数设置为false。
- 消息是一个过期的消息,超时无人消费。
- 要投地的队列信息堆满了,最早的消息可能成为死信。
如果该队列配置了dead-letter-exchange属性,指定了一个交换机,那么队列中死信就会投递到这个交换机中,而这个交换机被称为死信交换机(DLX)。
上述图片为死信交换机的流程 :当消息成为死信之后队列会将消息投递给死信交换机,然后投递到死信队列中。
4.1 死信交换机与RepublishMessageRecoverer不同点
RepublishMessageRecoverer的流程图:
差异: 在RepublishMessageRecoverer中所有的失败消息是由消费者投递的,但是在私信交换机中是由队列投递的,失败的消息是被消费者reject。
4.2 TTL
定义:TTL也就是Time-to-Live。如果一个队列中的消息TTL结束仍未消费,则会变成死信,ttl超时分为两种情况:
- 消息所在的队列中设置了存活时间。
- 消息本身设置了存活时间。
当消息所在的队列与消息本身同事都设置了存活时间,最终的存活时间由消息时间短的来决定。
4.3 设置延迟消息
死信交换机:
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "dl.queue",durable = "true"),
exchange = @Exchange(name = "dl.direct"),
key = "dl"
))
public void listenQlQueue(String msg) {
log.info("接收到 dl.name的延迟消息:{}",msg);
}
正常队列:
@Configuration
public class TTLMessageConfig {
@Bean
public DirectExchange ttlDirectExchange(){
return new DirectExchange("ttl.direct");
}
@Bean
public Queue ttlQueue(){
return QueueBuilder
.durable("ttl.queue")
.ttl(10000)
.deadLetterExchange("dl.direct")
.deadLetterRoutingKey("dl")
.build();
}
@Bean
public Binding ttlBinding(){
return BindingBuilder.bind(ttlQueue()).to(ttlDirectExchange()).with("ttl");
}
}
发送消息:
@Test
public void testTTLMessage() {
// 1.准备消息
Message message = MessageBuilder
.withBody("hello, ttl messsage".getBytes(StandardCharsets.UTF_8))
.setDeliveryMode(MessageDeliveryMode.PERSISTENT)
.setExpiration("5000") // 设置消息的超时时间
.build();
// 2.发送消息
rabbitTemplate.convertAndSend("ttl.direct", "ttl", message);
// 3.记录日志
log.info("消息已经成功发送!");
}
4.4 惰性队列
定义:惰性队列有如下特征:
- 接收到消息后直接存入磁盘而非内存。
- 消费者要消费消息才会从磁盘中读取并加载到内存。
- 支持数百万的消息存储。
4.4.1 设置惰性队列的两种方式
@Bean方式:
@Bean
public Queue lazyQueue() {
return QueueBuilder.durable("lazy.queue")
.lazy() // 开启x-queue-mode为lazy
.build();
}
注解方式:
@RabbitListener(queuesToDeclare = @Queue(
name = "lazy.queue",
durable = "true",
arguments = @Argument(name = "x-queue-mode", value = "lazy")
))
public void listenLazyQueue(String msg) {
log.info("接收到 lazy.queue的消息: {}",msg);
}