RabbitMQ学习笔记

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还有其他的一些属性,比如requireddefaultvalue等属性,顾名思义:

@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的消息确认机制分为两种:

  1. 生产者确认机制:生产者发送消息后,需要等待RabbitMQ服务器的确认消息,以确保消息已经被成功地发送到RabbitMQ服务器。如果RabbitMQ服务器没有收到消息或者消息发送失败,生产者会收到一个确认消息,从而可以进行重发或者其他处理。

  2. 消费者确认机制:消费者接收到消息后,需要向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);
    }

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值