文章目录

  • RabbitMQ 高级特性
    • 消息可靠性投递(可靠性发送)
      • 事务机制
        • 代码实现
      • 发送方确认机制
        • 为什么比事务性能好
        • 示例代码
        • 测试一下QPS
      • 持久化存储
      • TTL 队列
      • 死信队列(DLX)
      • 延迟队列
      • 消息不可达
        • 找不到队列,如何处理?
        • 备份交换机
    • 消息可靠性接收
      • 幂等
      • 可靠性消费
      • 消息顺序
      • 消费失败,重回队列
      • 消息者慢,限流
      • 单队列多消费者,消息分发
    • RabbitMQ 可靠性保证 总结
      • 可靠性传输语义
      • 如何业务上成功的消费一次
    • 摘录资料

RabbitMQ 高级特性

消息可靠性投递(可靠性发送)

当消息的生产者将消息发送出去之后,消息 到底有没有正确地到达服务器呢?如果不进行特殊配置,默认情况下发送消息的操作是不会返回任何信息给生产者的,也就是默认情况下生产者是不知道消息有没有正确地到达服务器。如果在消息到达服务器之前己经丢失,持久化操作也解决不了这个问题,因为消息根本没有到达服务器,何谈持久化?

针对以上的问题,RabbitMQ提供了以下的两种解决方案:

  • 通过事务机制实现:
  • 通过发送方确认 (publisher confirm) 机制实现

事务机制

RabbitMQ 客户端中与事务机制相关的方法有三个:

  1. channel.txSelect 开启事务
  2. channel.txCommit 提交事务
  3. channel.txRollback 事务回滚

和JDBC的事务三个步骤类似,都是三步走

代码实现

public static void main(String[] args) {Channel channel = ChannelFactory.getChannelInstance();logger.info("开启通道成功");try {channel.exchangeDeclare("tx-exchange", "direct");channel.txSelect();channel.basicPublish("tx-exchange", "tx", MessageProperties.PERSISTENT_BASIC, "hello tx".getBytes());int i = 1 / 0;channel.txCommit();logger.info("消息发送成功!");} catch (Exception e) {logger.error("消息发送失败", e);try {channel.txRollback();} catch (IOException e1) {logger.error("消息回滚失败", e1);}}ChannelFactory.closeChannel(channel);logger.info("关闭通道成功");}

事务确实能够解决消息发送方和 RabbitMQ 之间消息确认的问题,'只有消息成功被 RabbitMQ 接收,事务才能提交成功,
否则便可在捕获异常之后进行事务回滚,与此同时可以使用消息重发机制来保证消息不丢失。

除了这种方案保证消息发送的可靠性,还有其它什么方案呢?

从 AMQP 协议层面来看并没有更好的办法,但是 RabbitMQ 提供了一个改进方案,即发送方确认机制,详情请看下一节的介绍。

发送方确认机制

生产者将信道设置成 confmn C确认)模式,一旦信道进入 confmn 模式,所有在该信道上面发布的消息都会被指派一个唯一的 IDC从 l 开始),
一旦消息被投递到所有匹配的队列之后, RabbitMQ 就会发送一个确认 CBasic.Ack) 给生产者(包含消息的唯一 ID),这就使得生产者知晓消息已经正确到达了目的地了。
如果消息和队列是可持久化的,那么确认消息会在消息写入磁盘之后发出。
RabbitMQ 回传给生产者的确认消息中的 deliveryTag 包含了确认消息的序号,此外 RabbitMQ 也可以设置 channel.basicAck 方法中的 multiple 参数,表示到 这个序号之前的所有消息都己经得到了处理
候的确认之间的异同。

为什么比事务性能好

事务机制在一条消息发送之后会使发送端阻塞,以等待 RabbitMQ 的回应,之后才能继续发送下一条消息。

相比之下, 发送方确认机制最大的好处在于它是异步的,一旦发布一条消息,生产者应用程序就可以在等信道返回确认的同时继续发送下一条消息,当消息最终得到确认后,生产者应用程序便可以通过回调方法来处理该确认消息,
如果 RabbitMQ 因为自身内部错误导致消息丢失,就会发送一条 nack(Basic.Nack, not ack) 命令,生产者应用程序同样可以在回调方法中处理该 nack 命令。
生产者通过调用 channel.confirmSelect 方法(即 Confirm.Select 命令)将信道设置为 confirm 模式,
之后 RabbitMQ 会返回 Confirm.Select-Ok 命令表示同意生产者将当前信道设置为 confirm 模式。
所有被发送的后续消息都被 ack 或者 nack 一次,不会出现一条消息既被ack又被nack的情况,并且 RabbitMQ 也并没有对消息被 confirm 的快慢做任何保证。

示例代码

public static void main(String[] args) {Channel channel = ChannelFactory.getChannelInstance();logger.info("开启通道成功");try {channel.exchangeDeclare("confirm-exchange", "direct");// 开启Confirm模式AMQP.Confirm.SelectOk ok = channel.confirmSelect();channel.basicPublish("confirm-exchange", "confirm", MessageProperties.PERSISTENT_BASIC, "hello tx".getBytes());logger.info("消息发送成功!");} catch (Exception e) {logger.error("消息发送失败", e);}// 6. 设置监听channel.addConfirmListener(new ConfirmListener() {public void handleAck(long deliveryTag, boolean multiple) throws IOException {System.out.println("-------服务端 ACK Success--------");}public void handleNack(long deliveryTag, boolean multiple) throws IOException {System.err.println("-------服务端 ACK Failed--------");}});}

测试一下QPS

服务器环境 部署在windows(i78700 6C 3.2GHZ 16G)上的一个docker容器上面,运行时发现CPU飙到4.3GHZ,利用率30% ,因为分配给整个docker了2C。2/8 = 25%

  1. Confirm 机制

  1. 事务机制

docker 容器内部负载

可以看到上面,事务机制的时候的qps,会有二十倍性能的下降,这是为什么呢,我猜因为不是批量提交。

测试一下100条批量提交

基本和confirm持平。但是由于confirm机制是异步机制,也就是代码在顺序上正常执行,如果需要确认消息正常发送到broker,使用的是callback机制。和future类似。所以在现实编码中,使用这两种的哪一个,可以对照future的机制。

QPS略微下降的原理

对于持久化的消息来说,两者都需要等待消息确认落盘之后才会返回(调用 Linux内核的fsync方法)。在同步等待的方式下, publisher confirm 机制发送一条消息需要通 信交互的命令是 2 条:Basic.Publish 和 Basic .Ack; 事务机制是 3 条 :Basic.Publish、
Tx.Commmit/.Commit-Ok (或者 Tx.Rollback/.Rollback-Ok) , 事务机制多了一个命令帧报文的交互,所以 QPS 会略微下降。

持久化存储

常见的持久化就是数据库。那么在这里面,可以做持久化的大致有以下几个地方:

  1. exchange的持久化

    是通过在声明Exchange是将durable参数置为 true 实现的。

     /*** Actively declare a non-autodelete exchange with no extra arguments* @see com.rabbitmq.client.AMQP.Exchange.Declare* @see com.rabbitmq.client.AMQP.Exchange.DeclareOk* @param exchange the name of the exchange* @param type the exchange type* @param durable true if we are declaring a durable exchange (the exchange will survive a server restart)* @throws java.io.IOException if an error is encountered* @return a declaration-confirm method to indicate the exchange was successfully declared*/Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable) throws IOException;
    

    如果交换器不设置持久化,那么在 RabbitMQ服务重启之后,相关的交换器元数据会丢失。那么它具体会持久化到哪里呢?磁盘。

  2. Queue的持久化

      /*** Declare a queue* @see com.rabbitmq.client.AMQP.Queue.Declare* @see com.rabbitmq.client.AMQP.Queue.DeclareOk* @param queue the name of the queue* @param durable true if we are declaring a durable queue (the queue will survive a server restart)* @param exclusive true if we are declaring an exclusive queue (restricted to this connection)* @param autoDelete true if we are declaring an autodelete queue (server will delete it when no longer in use)* @param arguments other properties (construction arguments) for the queue* @return a declaration-confirm method to indicate the queue was successfully declared* @throws java.io.IOException if an error is encountered*/Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments) throws IOException;
    

    如果队列不设置持久化,那么在 RabbitMQ 服务重启之后,相关队列的元数据会丢失,
    此时数据也会丢失

  3. Message的持久化

    将消息的投递模式 (BasicProperties 中的 deliveryMode属性)设置为 2 即可实现消息的持久化。

    ​ 在持久化的消息正确存入 RabbitMQ 之后,还需要有一段时间(虽然很短,但是不 可忽视〉才能存入磁盘之中。 RabbitMQ并不会为每条消息都进行同步存盘(调用内核的fsync 方法)的处理,可能仅仅保存到操作系统缓存之中而不是物理磁盘之中。如果在这段时间内 RabbitMQ 服务节点发生了岩机、重启等异常情况,消息保存还没来得及落盘,那么这些消息将会丢失。
    ​ 这个问题怎么解决呢?这里可以引入RabbitMQ镜像队列机制,相当于配置了副本,如果主节点 (master) 在此特殊时间内挂掉,可以自动切换到从节点( slave ), 这样有效地保证了高可用性,除非整个集群都挂掉。虽然这样也不能完全保证RabbitMQ 消息 不丢失,但是配置了镜像队列要比没有配置镜像队列的可靠性要高很多,在实际生产环境中的 关键业务队列一般都会设置镜像队列。

    ​ 还可以在发送端引入上一节中(事务机制或者发送方确认机制)来保证消息己经正确地发送并存储至 RabbitMQ中,前提还要保证在调用 channel.basicPublish 方法的时候交换器能够将消息
    正确路由到相应的队列之中。

TTL 队列

通过 channel.queueDeclare 方法中的 x-expires参数可以控制队列被自动删除前处于未使用状态的时间。未使用的意思是队列上没有任何的消费者,队列也没有被重新声明,并且在过期时间段内也未调用过 Basic.Get 命令

 Map<String, Object> props = new HashMap<String, Object>();props.put("x-expires", 20 * 1000); // 设置 20s超时channel.queueDeclare("ttl-queue", false, false, false, props);

在没有Channel连接此Queue后的20s


可以看到**NOT FOUND **

RabbitMQ 会确保在过期时间到达后将队列删除,但是不能保证删除的动作有多及时 。在 RabbitMQ重启后, 持久化的队列的过期时间会被重新计算

死信队列(DLX)

DLX,全称为 Dead-Letter-Exchange,可以称之为死信交换器,也有人称之为死信邮箱。

当消息在一个队列中变成死信 (dead message) 之后,它能被重新被发送到另一个交换器中,这个交换器就是 DLX,绑定 DLX 的队列就称之为死信队列。消息被拒绝、消息过期、无法入队,该何去何从,死信队列这里来。

消息变成死信一般会有以下几种情况

  • 消息被拒绝 (Basic.Reject/Basic .Nack),井且不能重新入队(设置 requeue 参数为 false;)

     channel.basicConsume("normal-queue", false, new DefaultConsumer(channel){@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {// (消息id、multiple, requeue)channel.basicNack(envelope.getDeliveryTag(), false, false);System.out.println("拒绝收信成功。");}});
    

    multiple: 如果为true的话,拒绝所有消息,否则只拒绝deliveryTag这个指定的消息。

  • 消息过期

    生产者首先发送一条msg ,然后经过交换器NormalExchange存储到队列 NormalQueue 中 。由于队列 NormalQueue 设置了过期时间为 10s, 在这 10s 内没有消费者消费这条消息,那么判定这条消息为过期。由于设置了 DLX, 过期之时, 消息被丢给交换器 DLX Exchange1 中,这时找到与 DLX Exchange1 匹配的队列 DlxQueue, 最后消息被存储在 DLXQueue 这个死信队列中。等到后续有需要可以消费这个队列。

    Channel channel = ChannelFactory.getChannelInstance();try {channel.exchangeDeclare("normal-exchange", BuiltinExchangeType.DIRECT, false);channel.exchangeDeclare("dlx-exchange", BuiltinExchangeType.DIRECT, false);// 普通TTL队列设置Map<String, Object> arguments = new HashMap<String, Object>();arguments.put("x-message-ttl", 10 * 1000); // 设置 20s超时arguments.put("x-dead-letter-exchange", "dlx-exchange"); //arguments.put("x-dead-letter-routing-key", "dlx");channel.queueDeclare("normal-queue", false, false, false, arguments);channel.queueBind("normal-queue", "normal-exchange", "dlx");// 死信交换机、死信队列开启channel.queueDeclare("dlx-queue", true, false, false, null);channel.queueBind("dlx-queue", "dlx-exchange", "dlx");channel.basicPublish("normal-exchange", "dlx",null, "hello dlx".getBytes());// 不设置消费者channel.close();channel.getConnection().close();} catch (IOException e) {e.printStackTrace();} catch (TimeoutException e) {e.printStackTrace();}
    

延迟队列

延迟队列指的就是可以在固定时间长度之后才可以被消费到。

  • 在订单系统中, 一个用户下单之后通常有 30 分钟的时间进行支付,如果 30 分钟之内没有支付成功,那么这个订单将进行异常处理,这时就可以使用延迟队列来处理这些 订单了 。
  • 用户希望通过手机远程遥控家里的智能设备在指定的时间进行工作。这时候就可以将 用户指令发送到延迟队列,当指令设定的时间到了再将指令推送到智能设备。

延迟队列的实现就是通过TTL + DLX来实现,就是像我们上一节第二种实现一样。

消息不可达

​ 现实中,可能会遇到一些没有绑定队列的exchange,或者是超时队列已经失效了,这个时候消息没有成功消费,如果我们不进行处理,消息就会丢失。

找不到队列,如何处理?

RabbitMQ提供了一种机制,就是可以在发布消息的方法里设置mandatory为 true,这样在消息不可达的时候,会返回给生产者一个消息。

channel.basicPublish("mandatory-exchange", "confirm",true, false, MessageProperties.PERSISTENT_BASIC, "hello tx".getBytes());channel.addReturnListener(new ReturnListener() {@Overridepublic void handleReturn(int replyCode, String replyText, String exchange, String routingKey,AMQP.BasicProperties properties, byte[] body) throws IOException {System.out.println("没有到达目的地的消息::" + new String(body));}});

如果没有投递到目的地,就会进入ReturnListener的回调方法

备份交换机

如果我们嫌上面的麻烦,我们可以直接设置一个备份的交换机。

Map<String, Object> args = new HashMap<String, Object>();
args.put("a1ternate-exchange", "a1ternateExchange");
channe1.exchangeDec1are("norma1Exchange", "direct", true, fa1se, args);
channe1.exchangeDec1are("a1ternateExchange", "direct", true, fa1se, null);
  1. 如果设置的备份交换器不存在,客户端和 RabbitMQ 服务端都不会有异常出现,此时消息会丢失。

  2. 如果备份交换器没有绑定任何队列,客户端和 RabbitMQ 服务端都不会有异常出现,此 时消息会丢失。

  3. 如果备份交换器没有任何匹配的队列,客户端和 RabbitMQ 服务端都不会有异常出现,此时消息会丢失。

  4. 如果备份交换器和 mandatory 参数一起使用,那么 mandatory 参数无效。

消息可靠性接收

幂等

目前实现的最多的就是At least one语义,也就是保证消费者至少接收到一次消息,所以会存在重复消息的情况,需要我们业务代码进行幂等的处理.

  1. 可以给发送的消息一个唯一ID,将ID存储在分布式缓存(或数据库)中,这样在每次接收到消息之后,进行比较,看是否是重复的消息。
  • 需要保证缓存一定是高可用的

可靠性消费

假如消费者,在消费信息,然后ack了,但是刚开始处理就宕机了。

  1. 方案1: 如果消息特别重要的话,借助数据库存储每次接收到的消息,然后设置标志位为0,如果处理成功就为1,如果处理失败为2(人工介入排查错误)。开启一个分布式任务,检测标志位为0并且超时(>= 20min)的。

消息顺序

假如消费者处理的消息需要依赖发送者发送消息的顺序,那么就需要一些机制来保证了,比如Producer发送的是m1、m2、m3,那么Consumer接收到的应该也是m1、m2、m3。

消费失败,重回队列

 channel.basicConsume("normal-queue", false, new DefaultConsumer(channel){@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {// (消息id、multiple, requeue)channel.basicNack(envelope.getDeliveryTag(), false, true);//  (消息id, requeue)channel.basicReject(envelope.getDeliveryTag(), true);System.out.println("拒绝收信成功。");}});
  • basicReject 只能拒绝单条消息

  • basicNack 可以拒绝多条消息

消息者慢,限流

RabbitMQ可以在非自动确认消息(即设置autoAckfasle的情况)的前提下, 如果一定数目或者一定大小的消息未被确认前, 不进行消费新消息。可以通过consumer或者channel设置qos的值。其实这里的应用和Kafka差不多。

QoS(Quality of Service,服务质量)指一个网络能够利用各种基础技术,为指定的网络通信提供更好的服务能力, 是网络的一种安全机制, 是用来解决网络延迟和阻塞等问题的一种技术。

设置每次3条

public static void main(String[] args) throws Exception {Channel channel = ChannelFactory.getChannelInstance();channel.exchangeDeclare("quick-exchange", BuiltinExchangeType.DIRECT);channel.queueDeclare("quick-queue", false, false, false, null);channel.queueBind("quick-queue", "quick-exchange", "quick");// 设置 限流// prefetchSize 最大的消息大小// prefetchCount 最大的消息条数// global 针对所有的消费者channel.basicQos(0, 3, true);// 发送 10000条for (int i = 0; i < 10000; i++) {channel.basicPublish("quick-exchange", "quick", null, ("msg" + i).getBytes());}channel.basicConsume("quick-queue", false, new DefaultConsumer(channel){@Overridepublic void handleDelivery(java.lang.String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {logger.info(envelope.getDeliveryTag() + ":::::" + new String(body));channel.basicAck(envelope.getDeliveryTag(), false);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}});}
2019-05-28 22:23:58,899 pool-3-thread-4 [INFO ] com.wuhulala.rabbitmq.chapter2.qos.QosChannel$1.handleDelivery(QosChannel.java:52) 1:::::msg9169
2019-05-28 22:23:58,901 pool-5-thread-3 [INFO ] com.wuhulala.rabbitmq.chapter2.qos.QosChannel$1.handleDelivery(QosChannel.java:52) 1:::::msg9376
2019-05-28 22:23:59,097 pool-4-thread-3 [INFO ] com.wuhulala.rabbitmq.chapter2.qos.QosChannel$1.handleDelivery(QosChannel.java:52) 1:::::msg2776
2019-05-28 22:24:00,904 pool-3-thread-4 [INFO ] com.wuhulala.rabbitmq.chapter2.qos.QosChannel$1.handleDelivery(QosChannel.java:52) 2:::::msg9170
2019-05-28 22:24:00,905 pool-5-thread-3 [INFO ] com.wuhulala.rabbitmq.chapter2.qos.QosChannel$1.handleDelivery(QosChannel.java:52) 2:::::msg9377
2019-05-28 22:24:01,100 pool-4-thread-3 [INFO ] com.wuhulala.rabbitmq.chapter2.qos.QosChannel$1.handleDelivery(QosChannel.java:52) 2:::::msg2777
2019-05-28 22:24:02,905 pool-3-thread-4 [INFO ] com.wuhulala.rabbitmq.chapter2.qos.QosChannel$1.handleDelivery(QosChannel.java:52) 3:::::msg9171
2019-05-28 22:24:02,906 pool-5-thread-3 [INFO ] com.wuhulala.rabbitmq.chapter2.qos.QosChannel$1.handleDelivery(QosChannel.java:52) 3:::::msg9378

因为我们设置了限流为global,并且设置为3个。所以所有的消费者都会被限流,可以看到每消费3个,所有的消费者才会开始下一波的消费。

单队列多消费者,消息分发

单个队列,多个消费者的时候,队列会以轮训的方式将消息分别交给各个消费者去处理。但是如果消费者群里面的消费消息的能力不均匀的话,可以设置为1,这样每个消费者在ack之后。队列才会分配给它下一条消息。

 channel.basicQos(1);

RabbitMQ 可靠性保证 总结

可靠性传输语义

消息可靠传输一般是业务系统接入消息中间件时首要考虑的问题,一般消息中间件的消息 传输保障分为三个层级。

  • At most once 消息可能会丢,但绝不会重复传输
  • At least one 消息绝不会丢,但可能会重复传输
  • Exactly once 每条消息肯定会被传输一次且仅传输一次

RabbitMQ 支持其中的"最多一次"和"最少一次"。

如何业务上成功的消费一次

首先我们想一下这个消息从生产到消费的这一个完整的链路,然后梳理一下如何实现到消费者只成功消费一次

  1. 消息生产者必须保证消息可以传输到Broker。可以通过事务机制或者发送者确认机制,保证消息可以传 输到 RabbitMQ 中。
  2. Broker必须保证所有消息不会被丢失,及时没有对应的消费者或者队列。可以通过使用 mandatory 参数或者备份交换器来确保消息能够从交换器路由到队列中,进而能够保存下来而不会被丢弃。
  3. 消息和队列都需要进行持久化处理,以确保 RabbitMQ 服务器在遇到异常情况时,只要磁盘不坏或者非极端情况不会造成太大的影响。
  4. 消费者关闭自动确认,通过手动确认的方式去确认己经正确消费的消息,
  5. 对消息体进行设计,对于相同的消息,消息ID必须相同,从而保证消息重复消费的幂等性。
  6. 并且消费者端进行持久化存储+状态位保证业务上的成功消费。
  7. 可以根据消费者的情况进行限流或者多消费者的公平消费。

摘录资料

*《 RabbitMQ实战指南》 朱忠华

  • https://blog.csdn.net/love905661433/column/info/31759 RabbitMQ专栏 七夜丶雪
  • 源码 https://github.com/wuhulala/mq-collection/tree/master/rabbitmq/rabbitmq-chapter2-advance

RabbitMQ 高级特性(吐血猝死整理篇)相关推荐

  1. RabbitMQ(二):RabbitMQ高级特性

    RabbitMQ(二):RabbitMQ高级特性 RabbitMQ是目前非常热门的一款消息中间件,不管是互联网大厂还是中小企业都在大量使用.作为一名合格的开发者,有必要了解一下相关知识,RabbitM ...

  2. 3 RabbitMQ高级特性 3

    主要为大家讲解RabbitMQ的高级特性和实际场景应用, 包括消息如何保障 100% 的投递成功 ? 幂等性概念详解,在海量订单产生的业务高峰期,如何避免消息的重复消费问题? Confirm确认消息. ...

  3. RabbitMQ高级特性

    文章目录 1. 简述 2. 特性示例: 2.1 消息可靠性投递 2.2 Consumer Ack 2.3 消费端限流 2.4 TTL 2.5 死信队列 2.6 延迟队列 1. 简述 在rabbitMQ ...

  4. 【消息中间件】RabbitMQ 高级特性与应用问题

    消息的可靠投递 在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景.RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式. confirm 确认模式 r ...

  5. 消息中间件--RabbitMQ ---高级特性之消费端ACK与重回队列

    什么是消费端的ACK和重回队列? 消费端的手工ACK和NACK 消费端进行消费的时候,如果由于业务异常我们可以进行日志的记录,然后进行补偿 如果由于服务器宕机等严重问题,那我们就需要手工进行ACK保障 ...

  6. RabbitMQ高级特性-惰性队列

    目录 一.消息堆积问题 二.解决消息堆积的三种思路 三.惰性队列 1.命令行修改惰性队列 2.用SpringAMQP声明惰性队列 @Bean的方式 注解方式 测试发送消息 3.惰性队列的优点 4.惰性 ...

  7. 1-8 (4). RabbitMQ高级特性-消费端ACK

    Consumer ACK 指Acknowledge,确认 有三种方式: (1)自动确认:acknowledge="none"(默认) (2)手动确认:acknowledge=&qu ...

  8. rabbitmq高级特性(消息手动确认)

    为了保证消息不丢失,从生产者两种模式(确认模式和返回模式)到消费者手动确认的一个大整合 生产者 1.maven依赖 <dependency><groupId>org.sprin ...

  9. RabbitMQ(13)RabbitMQ高级特性:TTL

    TTL全称Time To Live (存活时间/过期时间). 当消息到达存活时间后,还没有被消费,会被自动清除.. RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间. ...

最新文章

  1. 软件访问转向本地_我是如何从完整的初学者转向软件开发人员的,以及如何做到的...
  2. python基础30个常用代码大全-Python3列表内置方法大全及示例代码小结
  3. 分布式与人工智能课程(part12)--机器学习案例入门
  4. 我也说说宏定义likely()和unlikely()
  5. go语言中错误处理方式
  6. unity 线程断点时卡机_Java使用JDI进行线上程序断点信息记录
  7. 杭电oj-----Nightmare(BFS)
  8. 深度学习之遥感变化检测数据集整理
  9. 计算机核心期刊加拿大,ssci或cssci期刊北京大学图书馆版核心期刊国外学术.doc...
  10. 自制solidworks图框步骤_SolidWorks教你如何快速制作工程图模板
  11. 腾讯笔试题——java题总结无答案
  12. 芯片供应商:芯片一级供应商分类和安全芯片库存
  13. 阿卡迪亚大学计算机专业好考吗,加拿大阿卡迪亚大学热门专业介绍
  14. 【图像增强】Frangi滤波器血管图像增强【含Matlab源码 2108期】
  15. iPhone12、iPhone12 Pro、iPhone12 Max、iPhone12 Pro Max 详细参数配置
  16. 一些IGBT驱动芯片对比
  17. 双硬盘安装win10和linux双系统,双硬盘安装win10+ubuntu18心得
  18. CSS控制,彩色图片变灰色
  19. 赛迪顾问《2021-2022年中国政务云市场研究年度报告》发布 华云数据跃居行业领军者
  20. 【新手教程】51Sim-One Cloud 2.0 构建标准案例2.0场景

热门文章

  1. 2012年度博客大赛之我见
  2. 坚果云+svn实现异地非局域网个人代码版本管理
  3. Ubuntu定时清理缓存
  4. 关于./configure的使用
  5. 什么是远程办公,如何挑选远程办公软件
  6. easyUI Layout
  7. k2677场效应管参数引脚_常用场效应管型号及参数表
  8. mini2440 uboot烧写uImage
  9. what is long tail effect
  10. 封建日本挑战赛:电影/视觉特效角色获奖者访谈