关于java:RabbitMQ-中的消息会过期吗

3次阅读

共计 5068 个字符,预计需要花费 13 分钟才能阅读完成。

@[toc]
RabbitMQ 中的音讯长期未被生产会过期吗?用过 RabbitMQ 的小伙伴可能都有这样的疑难,明天松哥就来和大家捋一捋这个问题。

1. 默认状况

首先咱们来看看默认状况。

默认状况下,音讯是不会过期的,也就是咱们素日里在音讯发送时,如果不设置任何音讯过期的相干参数,那么音讯是不会过期的,即便音讯没被生产掉,也会始终存储在队列中。

这种状况具体代码就不必我再演示了吧,松哥之前的文章但凡波及到 RabbitMQ 的,基本上都是这样的。

2. TTL

TTL(Time-To-Live),音讯存活的工夫,即音讯的有效期。如果咱们心愿音讯可能有一个存活工夫,那么咱们能够通过设置 TTL 来实现这一需要。如果音讯的存活工夫超过了 TTL 并且还没有被音讯,此时音讯就会变成 死信 ,对于 死信 以及 死信队列,松哥前面再和大家介绍。

TTL 的设置有两种不同的形式:

  1. 在申明队列的时候,咱们能够在队列属性中设置音讯的有效期,这样所有进入该队列的音讯都会有一个雷同的有效期。
  2. 在发送音讯的时候设置音讯的有效期,这样不同的音讯就具备不同的有效期。

那如果两个都设置了呢?

以工夫短的为准。

当咱们设置了音讯有效期后,音讯过期了就会被从队列中删除了(进入到死信队列,后文一样,不再标注),然而两种形式对应的删除机会有一些差别:

  1. 对于第一种形式,当音讯队列设置过期工夫的时候,那么音讯过期了就会被删除,因为音讯进入 RabbitMQ 后是存在一个音讯队列中,队列的头部是最早要过期的音讯,所以 RabbitMQ 只须要一个定时工作,从头部开始扫描是否有过期音讯,有的话就间接删除。
  2. 对于第二种形式,当音讯过期后并不会立马被删除,而是当音讯要投递给消费者的时候才会去删除,因为第二种形式,每条音讯的过期工夫都不一样,想要晓得哪条音讯过期,必须要遍历队列中的所有音讯能力实现,当音讯比拟多时这样就比拟消耗性能,因而对于第二种形式,当音讯要投递给消费者的时候才去删除。

介绍完 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);
    }
}

这个配置类次要干了三件事:配置音讯队列、配置交换机以及将两者绑定在一起。

  1. 首先配置一个音讯队列,new 一个 Queue:第一个参数是音讯队列的名字;第二个参数示意音讯是否长久化;第三个参数示意音讯队列是否排他,个别咱们都是设置为 false,即不排他;第四个参数示意如果该队列没有任何订阅的消费者的话,该队列会被主动删除,个别实用于长期队列。
  2. 配置一个 DirectExchange 交换机。
  3. 将交换机和队列绑定到一起。

这段配置应该很简略,没啥好解释的,有一个排他性,松哥这里略微多说两句:

对于排他性,如果设置为 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
正文完
 0