RabbitMQ-死信队列

RabbitMQ深入 —— 死信队列 前面荔枝梳理了RabbitMQ中的普通队列、交换机以及相关的知识,在这篇文章中荔枝将会梳理RabbitMQ的一个重要的队列 —— 死信队列,主要了解消息流转到死信队列的三种的方式以及相应的实现demo。希望能帮助到有需要的小伙伴~~~ 阅读详情

以下单打车为例,用户在下单后,寻找附近的车辆,有一个司机接单了,但是没有在规定的时间到来,导致订单超时了。这时候平台就会再次寻找附近的车辆,下单通知附件的车辆来接你。

这类的场景如果放到MQ上,能实现吗?结合之前的实现已经可以做到订单超时了,但是超时后,目前还没有办法处理,这就需要使用死信队列了。

DLX,全称Dead-Letter-Exchange,死信交换器。消息在一个队列中变成死信(Dead Letter)之后,被重新发送到一个特殊的交换器(DLX)中,同时DLX的队列就称为“死信队列”。

以下几种情况导致消息变为死信:

  1. 消息被拒绝(Basic.Reject/Basic.Nack),并设置requeue参数为false.
  2. 消息过期。
  3. 队列达到最大长度。

对于RabbitMQ来说,DLX是一个非常有用的特性。它可以处理异常您那个况下,消息不能够被消息者正确消费(消费者调用了Basic.Nack或者Basic.Reject)而被置入死信队列中,后续分析程序可以通过消费死信队列中的内容来分析当时所遇到的异常情况。进而可以改善和优化系统。

9.1 被拒绝的消息

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class DlxRejectProduct {

  public static void main(String[] args) throws Exception {
    ConnectionFactory factory = new ConnectionFactory();

    factory.setUri("amqp://root:123456@node1:5672/%2f");

    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();

    // 定义测试消息过期的队列和交换器
    channel.exchangeDeclare("dlx.biz.reject.ex", BuiltinExchangeType.FANOUT, false);
    Map<String, Object> argument = new HashMap<>();
    // 当消息过期后,放置于死信队列
    argument.put("x-dead-letter-exchange", "dlx.dead.ex");
    // 设置队列所关联的死信交换器的routingKey,如果没有特殊指定,使用原队列的routingKey
    argument.put("x-dead-letter-routing-key", "rk.dlx.reject");
    channel.queueDeclare("dlx.biz.reject.qu", false, false, false, argument);
    channel.queueBind("dlx.biz.reject.qu", "dlx.biz.reject.ex", "dlx.biz.reject.rk");

    // 定义死信交换器和数据
    channel.exchangeDeclare("dlx.dead.ex", BuiltinExchangeType.DIRECT, true);
    // 用于接收过期后消息的队列
    channel.queueDeclare("dlx.reject.qu", false, false, false, null);
    // 将用于接收过期消息队列与交换器相绑定
    channel.queueBind("dlx.reject.qu", "dlx.dead.ex", "rk.dlx.reject");

    channel.basicConsume(
        "dlx.biz.reject.qu",
        false,
        new DefaultConsumer(channel) {
          @Override
          public void handleDelivery(
              String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
              throws IOException {
            // 进行消息的拒绝,并且不进入队列
            channel.basicReject(envelope.getDeliveryTag(), false);
          }
        });
  }
}

定义了一个业务交换器和队列,并将其绑定,在消费端将消息拒绝,并且不重新加入队列。在定义业务队列时,设置了死信交换器即消息拒绝后放置的放置的交换器,并设置了死信的路由key。

然后再定义了死信交换器以及对应拒绝后的接收消息的队列。

当消息到达消费端后,由于开启了手动ACK确认,会进入处理,而客户端的处理是拒绝消息,并且不重新放回队列,就会被放入到死信交换器dlx.dead.ex中,而这个消息的死信路由key为rk.dlx.reject,而此路由key绑定了队列dlx.reject.qu,这样就看到了消息进入了最终我们看到的队列 dlx.reject.qu中。

在这里插入图片描述

观察下队列的情况:

[root@nullnull-os rabbitmq]#  rabbitmqctl list_queues name,messages_ready,messages_unacknowledged,messages,consumers,policy  --formatter pretty_table
Timeout: 60.0 seconds ...
Listing queues for vhost / ...
┌───────────────────┬────────────────┬─────────────────────────┬──────────┬───────────┬────────┐
│ name              │ messages_ready │ messages_unacknowledged │ messages │ consumers │ policy │
├───────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.biz.reject.qu │ 0              │ 0                       │ 0        │ 1         │        │
├───────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.reject.qu     │ 1              │ 0                       │ 1        │ 0         │        │
└───────────────────┴────────────────┴─────────────────────────┴──────────┴───────────┴────────┘
[root@nullnull-os rabbitmq]# 

通过观察可以发现,消息已经进入了死信队列后的交换器。

9.2 过期的消息

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class DlxExpireProduct {

  public static void main(String[] args) throws Exception {
    ConnectionFactory factory = new ConnectionFactory();

    factory.setUri("amqp://root:123456@node1:5672/%2f");

    try (Connection connection = factory.newConnection();
        Channel channel = connection.createChannel(); ) {

      // 定义测试消息过期的队列和交换器
      channel.exchangeDeclare("dlx.biz.expire.ex", BuiltinExchangeType.FANOUT, false);
      Map<String, Object> argument = new HashMap<>();
      // 消息10秒过期
      argument.put("x-message-ttl", 10000);
      // 当消息过期后,放置于死信队列
      argument.put("x-dead-letter-exchange", "dlx.dead.ex");
      // 设置队列所关联的死信交换器的routingKey,如果没有特殊指定,使用原队列的routingKey
      argument.put("x-dead-letter-routing-key", "rk.dlx.expire");
      channel.queueDeclare("dlx.biz.expire.qu", false, false, false, argument);
      channel.queueBind("dlx.biz.expire.qu", "dlx.biz.expire.ex", "dlx.biz.expire.rk");

      // 定义死信交换器和数据
      channel.exchangeDeclare("dlx.dead.ex", BuiltinExchangeType.DIRECT, true);
      // 用于接收过期后消息的队列
      channel.queueDeclare("dlx.expire.qu", false, false, false, null);
      // 将用于接收过期消息队列与交换器相绑定
      channel.queueBind("dlx.expire.qu", "dlx.dead.ex", "rk.dlx.expire");

      // 测试过期消息的发送
      String msgExpire = "测试过期消息";
      channel.basicPublish(
          "dlx.biz.expire.ex", "", null, msgExpire.getBytes(StandardCharsets.UTF_8));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

在队列上设置消息10秒过期。并设置了过期绑定的死信交换器和key。

当消息过期时,就会被放入到dlx.dead.ex交换器,并且此消息设置了死信的路由key为rk.dlx.expire,而此路由key绑定了dlx.expire.qu这个队列,所以消息最终就发送到了dlx.expire.qu中

观察队列中的消息

[root@nullnull-os rabbitmq]#  rabbitmqctl list_queues name,messages_ready,messages_unacknowledged,messages,consumers,policy  --formatter pretty_table
Timeout: 60.0 seconds ...
Listing queues for vhost / ...
┌───────────────────┬────────────────┬─────────────────────────┬──────────┬───────────┬────────┐
│ name              │ messages_ready │ messages_unacknowledged │ messages │ consumers │ policy │
├───────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.expire.qu     │ 0              │ 0                       │ 0        │ 0         │        │
├───────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.biz.expire.qu │ 1              │ 0                       │ 1        │ 0         │        │
├───────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.biz.reject.qu │ 0              │ 0                       │ 0        │ 0         │        │
├───────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.reject.qu     │ 1              │ 0                       │ 1        │ 0         │        │
└───────────────────┴────────────────┴─────────────────────────┴──────────┴───────────┴────────┘
[root@nullnull-os rabbitmq]# 

等待10秒后,再查看队列:

[root@nullnull-os rabbitmq]#  rabbitmqctl list_queues name,messages_ready,messages_unacknowledged,messages,consumers,policy  --formatter pretty_table
Timeout: 60.0 seconds ...
Listing queues for vhost / ...
┌───────────────────┬────────────────┬─────────────────────────┬──────────┬───────────┬────────┐
│ name              │ messages_ready │ messages_unacknowledged │ messages │ consumers │ policy │
├───────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.expire.qu     │ 1              │ 0                       │ 1        │ 0         │        │
├───────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.biz.expire.qu │ 0              │ 0                       │ 0        │ 0         │        │
├───────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.biz.reject.qu │ 0              │ 0                       │ 0        │ 0         │        │
├───────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.reject.qu     │ 1              │ 0                       │ 1        │ 0         │        │
└───────────────────┴────────────────┴─────────────────────────┴──────────┴───────────┴────────┘
[root@nullnull-os rabbitmq]# 

可以发现,消息已经进入了死信队列了。

9.3 超过队列的长度的消息

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class DlxMaxLengthProduct {

  public static void main(String[] args) throws Exception {
    ConnectionFactory factory = new ConnectionFactory();

    factory.setUri("amqp://root:123456@node1:5672/%2f");

    try (Connection connection = factory.newConnection();
        Channel channel = connection.createChannel(); ) {

      // 定义测试消息过期的队列和交换器
      channel.exchangeDeclare("dlx.biz.max.length.ex", BuiltinExchangeType.FANOUT, false);
      Map<String, Object> argument = new HashMap<>();
      // 当消息过期后,放置于死信队列
      argument.put("x-dead-letter-exchange", "dlx.dead.ex");
      // 设置队列所关联的死信交换器的routingKey,如果没有特殊指定,使用原队列的routingKey
      argument.put("x-dead-letter-routing-key", "rk.dlx.max.length");
      // 定义消息的最大长度为2,超过2个,第三个即为死信消息
      argument.put("x-max-length", 2);
      channel.queueDeclare("dlx.biz.max.length.qu", false, false, false, argument);
      channel.queueBind("dlx.biz.max.length.qu", "dlx.biz.max.length.ex", "dlx.biz.max.length.rk");

      // 定义死信交换器和数据
      channel.exchangeDeclare("dlx.dead.ex", BuiltinExchangeType.DIRECT, true);
      // 用于接收过期后消息的队列
      channel.queueDeclare("dlx.max.length.qu", false, false, false, null);
      // 将用于接收过期消息队列与交换器相绑定
      channel.queueBind("dlx.max.length.qu", "dlx.dead.ex", "rk.dlx.max.length");


      String push1 = "测试发送消息1";
      String push2 = "测试发送消息2";
      String push3 = "测试发送消息3";
      channel.basicPublish("dlx.biz.max.length.ex","",null,push1.getBytes(StandardCharsets.UTF_8));
      channel.basicPublish("dlx.biz.max.length.ex","",null,push2.getBytes(StandardCharsets.UTF_8));
      channel.basicPublish("dlx.biz.max.length.ex","",null,push3.getBytes(StandardCharsets.UTF_8));

    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

通过指定消息队列的长度为2,即第三个消息就会进入死信队列。规则还是同之前的过期和拒绝一样。

[root@nullnull-os rabbitmq]#  rabbitmqctl list_queues name,messages_ready,messages_unacknowledged,messages,consumers,policy  --formatter pretty_table
Timeout: 60.0 seconds ...
Listing queues for vhost / ...
┌───────────────────────┬────────────────┬─────────────────────────┬──────────┬───────────┬────────┐
│ name                  │ messages_ready │ messages_unacknowledged │ messages │ consumers │ policy │
├───────────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.expire.qu         │ 1              │ 0                       │ 1        │ 0         │        │
├───────────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.biz.expire.qu     │ 0              │ 0                       │ 0        │ 0         │        │
├───────────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.biz.reject.qu     │ 0              │ 0                       │ 0        │ 0         │        │
├───────────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.max.length.qu     │ 1              │ 0                       │ 1        │ 0         │        │
├───────────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.biz.max.length.qu │ 2              │ 0                       │ 2        │ 0         │        │
├───────────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.reject.qu         │ 1              │ 0                       │ 1        │ 0         │        │
└───────────────────────┴────────────────┴─────────────────────────┴──────────┴───────────┴────────┘
[root@nullnull-os rabbitmq]# 

最终的消息是两个在业务队列,而超过最大长度的消息在死信队列中。

9.4 SpringBoot使用死信队列

maven导入

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
            <version>2.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.2.8.RELEASE</version>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
            <version>2.2.7.RELEASE</version>
            <scope>test</scope>
        </dependency>

连接配制

spring:
  application:
    name: dlx
  rabbitmq:
    host: node1
    port: 5672
    virtual-host: /
    username: root
    password: 123456

主启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DlxApplication {
  public static void main(String[] args) {
    SpringApplication.run(DlxApplication.class, args);
  }
}

队列配制

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class DlxConfig {

  /**
   * 业务队列,并指定了死信交换器
   *
   * @return
   */
  @Bean
  public Queue bizQueue() {
    Map<String, Object> argument = new HashMap<>();

    // 消息在10秒后过期
    argument.put("x-message-ttl", 5000);
    // 设置该队列所关联的死信交换器,当消息超过10秒没有消费,则加入死信队列
    argument.put("x-dead-letter-exchange", "dlx.springboot.ex");
    // 设置该队列所关联的死信交换器的routingKey,如果没有特殊的指定,使用原队列的routingKey.
    argument.put("x-dead-letter-routing-key", "dlx.springboot.rk");
    Queue queue = new Queue("dlx.spring.biz.qu", false, false, false, argument);
    return queue;
  }

  /**
   * 业务交换器
   *
   * @return
   */
  @Bean
  public Exchange bizExchange() {
    return new DirectExchange("dlx.spring.biz.ex", false, false, null);
  }

  @Bean
  public Binding bizBind() {
    return BindingBuilder.bind(bizQueue()).to(bizExchange()).with("dlx.spring.biz.rk").noargs();
  }

  /**
   * 死信队列
   *
   * @return
   */
  @Bean
  public Queue queueDlx() {
    return new Queue("dlx.springboot.expire.qu", false, false, false);
  }

  @Bean
  public Exchange exchangeDlx() {
    return new DirectExchange("dlx.springboot.ex", true, false, null);
  }

  @Bean
  public Binding bindDlx() {
    return BindingBuilder.bind(queueDlx()).to(exchangeDlx()).with("dlx.springboot.rk").noargs();
  }
}

控制层处理

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.StandardCharsets;


@RestController
public class BizController {

  @Autowired private AmqpTemplate template;

  @RequestMapping("/expire-dlx")
  public String expireDlx() {
    String msg = "测试发送消息,10秒超时";
    template.convertAndSend(
        "dlx.spring.biz.ex", "dlx.spring.biz.rk", msg.getBytes(StandardCharsets.UTF_8));

    return "expire-dlx";
  }

  @RequestMapping("/dlx/get")
  public String sendDlxMsg() {
    byte[] getMsg = (byte[]) (template.receiveAndConvert("dlx.springboot.expire.qu"));

    return new String(getMsg, StandardCharsets.UTF_8);
  }
}

启动项目

然后在浏览器中输入:http://127.0.0.1:8080/expire-dlx

观察队列信息:

[root@nullnull-os rabbitmq]#  rabbitmqctl list_queues name,messages_ready,messages_unacknowledged,messages,consumers,policy  --formatter pretty_table
Timeout: 60.0 seconds ...
Listing queues for vhost / ...
┌──────────────────────────┬────────────────┬─────────────────────────┬──────────┬───────────┬────────┐
│ name                     │ messages_ready │ messages_unacknowledged │ messages │ consumers │ policy │
├──────────────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.springboot.expire.qu │ 0              │ 0                       │ 0        │ 0         │        │
├──────────────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.spring.biz.qu        │ 1              │ 0                       │ 1        │ 0         │        │
└──────────────────────────┴────────────────┴─────────────────────────┴──────────┴───────────┴────────┘
[root@nullnull-os rabbitmq]# 

此时数据在业务队列中。等待5秒,再观察队列:

[root@nullnull-os rabbitmq]#  rabbitmqctl list_queues name,messages_ready,messages_unacknowledged,messages,consumers,policy  --formatter pretty_table
Timeout: 60.0 seconds ...
Listing queues for vhost / ...
┌──────────────────────────┬────────────────┬─────────────────────────┬──────────┬───────────┬────────┐
│ name                     │ messages_ready │ messages_unacknowledged │ messages │ consumers │ policy │
├──────────────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.springboot.expire.qu │ 1              │ 0                       │ 1        │ 0         │        │
├──────────────────────────┼────────────────┼─────────────────────────┼──────────┼───────────┼────────┤
│ dlx.spring.biz.qu        │ 0              │ 0                       │ 0        │ 0         │        │
└──────────────────────────┴────────────────┴─────────────────────────┴──────────┴───────────┴────────┘
[root@nullnull-os rabbitmq]#

发现数据已经进入了死信队列中。

在浏览器中访问另外的一个接口, http://127.0.0.1:8080/dlx/get

便能得到发送的数据信息:

测试发送消息,10秒超时

SpringBoot整合Rabbitmq之死信队列 目录死信队列死信从何而来 死信队列 死信:无法被消费方消费掉的消息,称为死信。如果死信一直留在队列中,会导致一直被消费,却从不消费成功。所以 rabbitmq 专门开辟了一个来存放死信的队列,叫死信队列(DLX,dead-letter-exchange) 死信从何而来 消息被拒绝(调用 basicNack() 或 basicReject(),并且参数 requeue = false) 消息过期,过了 TTL 存活时间 队列设置了 x-max-length 最大消息数量且当前队列中的消息已经达到了这个数量, 阅读详情

相关推荐

RabbitMQ(九)死信队列

RabbitMQ(九)死信队列

ACGkaka的博客 2008

RabbitMQ(四)RabbitMQ死信邮箱(DLX)

DLX,Dead-Letter-Exchange(死信邮箱) 利用DLX,当消息在一个队列中变成死信后,它能被重新publish到另一个Exchange,这个Exchange就是DLX。消息变成死信一向有以下几种情况: 消息被拒绝(basic.reject or basic.nack)并且requeue=false 消息TTL过期 队列达到最大长度 DLX也是一下正常的Exchange同一般的Exchange没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性,当这个队列中有死信时,Rabbit

很多时候,你缺少的不是知识而是热情 1万+

ESP32内存优化实战:关闭WiFi/LWIP功能释放37KB IRAM的隐藏技巧

本文针对ESP32开发中IRAM内存不足的难题,提供了一种通过关闭WiFi和LWIP协议栈的IRAM优化功能来释放宝贵内存空间的实战技巧。该方法最高可释放约37KB的IRAM,有效解决复杂项目(如WiFi+BLE+语音识别)的编译失败问题,并详细阐述了配置步骤、性能权衡与测试验证方法。

redis7keeper的博客 131

Spring Cloud Stream整合Rabbit之重复投递

点击上方关注 “终端研发部”设为“星标”,和你一起掌握更多数据库知识SpringCloudStream整合Rabbit时,消费端在处理失败时,如果需要进行重试,可以有如下几种重试机制:方法1(默认):当消费端在处理消息时抛出异常,那么默认会在当前线程的3次的Retry。该方法是默认的,可以通过修改配置文件,指定channel下的参数,例如:1 2 3 4 5 6 7 8 9 10spring:...

这个时代,作为程序员可能要学习小程序 904

prokka-原核及病毒基因组高效便捷注释

文章目录简介安装使用参考 简介 Prokka: rapid prokaryotic genome annotation 全基因组注释是在一组基因组DNA序列中识别感兴趣的特征,并用有用的信息标记它们的过程。Prokka是一款软件工具,可以快速注释细菌、古生菌和病毒基因组,并生成符合标准的输出文件。 安装 conda install -c conda-forge -c bioconda -c defaults prokka Test Type prokka and it should output its

Neptuneyut的博客 3312

Qt 插件开发 plugin 超详细 有源码

Qt 插件化开发框架类似于前后端的微服务的场景,授权哪个微服务则前端可以使用哪个微服务,插件就行硬件插卡一样,可以被随时删除、插入和修改,所以结构很灵活,容易修改,方便软件的升级和维护;插件主要面向接口编程,无需访问.lib文件,热插拔、利于团队开发。即使在程序运行时.dll不存在,也可以正常启动,只是相应插件功能无法正常使用而已;动态库需要访问.lib文件,而且在程序运行时必须保证.lib存在,否则无法正常启动;

q610098308的专栏 6547

RabbitMQ延迟消息问题(含Demo工程)

延迟队列的使用场景包括: 1、延迟发送短信。 2、用户下单,如果用户在15 分钟内未支付,则自动取消。 3、预约工作会议,20分钟后自动通知所有参会人员。

一切总会归于平淡 5034

从Xilinx FPGA到MIPI DSI LCD屏:手撸代码的实战经验分享

本文分享了使用Xilinx FPGA驱动MIPI DSI LCD屏的实战经验。作者从依赖官方IP核失败,到与底层D-PHY IP缠斗,最终通过手撸代码,直接配置GTY收发器原语并实现完整的D-PHY与DSI协议栈,成功点亮屏幕。文章详细剖析了协议细节、Vivado工具链的调试技巧与避坑指南,为FPGA工程师提供了从理论到实践的宝贵参考。

weixin_29188997的博客 400

RabbitMQ - 死信队列,延时队列

而在第二种方法中,即使消息过期,也不会马上从队列中抹去,因为每条消息是否过期是在即将投递到消费者之前判定的。参数,那么它会被发送到x-dead-letter-exchange对应值的交换器上,这个交换器就称之为死信交换器,与这个死信交换器绑定的队列就是死信队列。DLX 全称(Dead-Letter-Exchange),称之为死信交换器,当消息变成一个死信之后,如果这个消息所在的队列存在。消费者二监听的死信队列收到消息。进入死信队列之后,headers 加了一些死信相关的信息,包括原队列以及进入死信的原因。

MinggeQingchun的博客 4140

ESP32脑机接口开发板:EEG信号采集与应用开发指南

脑机接口(BCI)技术通过采集脑电图(EEG)信号实现人机交互,其核心在于高精度生物电信号采集与实时处理。基于ESP32微控制器的开发平台结合专业级ADS1299 ADC芯片,可构建8通道EEG信号采集系统,支持250SPS采样率与可编程增益。这种嵌入式解决方案在科研教育领域具有显著优势,既能满足信号质量要求,又大幅降低开发门槛。典型应用包括实时脑电波可视化、注意力检测等BCI场景,通过Python生态工具链(如BrainFlow、LSL)可快速实现数据处理。开发时需注意电极阻抗控制(<10kΩ)和50/6

weixin_42518874的博客 346

39、信号流图:解决线性代数方程组的图论方法

本文介绍了信号流图理论在求解线性代数方程组中的应用,重点阐述了Coates方法和Mason方法的原理、步骤及对比分析。通过图论视角,将矩阵运算转化为对有向图结构的分析,利用1-因子、1-阶连接、前向路径和反馈回路等概念,推导出方程组解的拓扑公式。文章结合具体实例演示两种方法的计算过程,并总结其在电路分析、控制系统和通信系统等领域的拓展应用,展示了信号流图理论的直观性与有效性。

pear55的博客 88

RabbitMQ之死信交换机

在了解死信交换机之前我们先了解什么是死信消息变成死信一般是由于以下几种情况:1.消息在处理过程中多次重试仍然失败,达到预定的重试次数上限;2.),并且设置requeue参数为false;3.消息在队列中等待时间过长,超过了设置的过期时间;4.当消息队列的长度达到上限时,新的消息可能成为死信。死信交换机是消息队列系统中的一种特殊交换机,用于处理那些无法被正常消费的消息。当消息满足一定的条件,例如重试次数达到上限或者处理失败,就会被标记为死信(Dead Letter)并被发送到死信交换机。

ChatYU的博客 3790

智慧城市中的CV:交通流量监控与行人检测

智慧城市中的CV:交通流量监控与行人检测 ,人工智能,计算机视觉,大模型,AI,计算机视觉作为人工智能的重要分支,通过分析和理解图像或视频数据,为智慧城市提供了强大的感知能力。在交通管理领域,计算机视觉技术可实现交通流量监控、行人检测、违章行为识别等功能,助力构建高效、安全、绿色的城市交通系统。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 5万+

RabbitMQ死信队列

目录 一、死信队列是什么 二、如何配置死信队列 三、死信消息的变化 四、死信队列应用场景 五、总结 RabbitMQ是流行的开源消息队列系统,使用erlang语言开发,由于其社区活跃度高,维护更新较快,性能稳定,深得很多企业的欢心(当然,也包括我现在所在公司【手动滑稽】)。 为了保证订单业务的消息数据不丢失,需要使用到RabbitMQ的死信队列机制,当消息消费发生异常时,将消息投入死信队列中。但由于对死信队列的概念及配置不熟悉,导致曾一度陷入百度的汪洋大海,无法自拔,很多文章都看起来可行,但

半桶水的博客 2万+

Python爬虫实战:利用Playwright与异步技术高效抓取链家十万级二手房数据

在开始编写代码之前,我们首先需要为项目选择合适的技术栈。1. 传统方案:Requests + BeautifulSoup优点: 简单、轻量、学习成本低。直接发送HTTP请求获取HTML,然后解析。缺点: 对于大量由JavaScript动态渲染的内容无能为力。现代网站(包括链家)大量使用JS来加载数据和实现交互,简单的HTTP请求无法获取到完整的页面内容。此外,容易被基于IP或请求头特征的反爬机制拦截。2. 进阶方案:Selenium优点。

2201_76125261的博客 891

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Rabbitmq死信队列

死信队列概念 死信,就是无法被消费的消息,一般来说, producer将消息投递到broker或者直接到queue里了, consumer从queue取出消息 进行消费,但某些时候由于特定的原因导致queue中的某些消息无法被消费,这样的消息如果没有后续的处理,就变成了死信, 有死信自然就有了死信队列。 应用场景:为了保证订单业务的消息数据不丢失,需要使用到RabbitMQ的死信队列机制,当消息消费发生异常时, 将消息投入死信队列中.还有比如说:用户在商城下单成功并点击去支付后在指定时间未支付时自动失效 死

我的身体只需要很少的粮食就能养活;而我的灵魂却非常地贪婪,需要日月星辰、山川湖泊和大自然的滋养。 1万+

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

RabbitMQ 死信队列详解

一、死信的概念 死信,顾名思义就是无法被消费的消息。一般来说,Producer 将消息投递到 Broker 或者直接到 Queue 里了,Consumer 从 Queue 取出消息进行消费,但某些时候由于特定的原因导致 Queue 中的某些消息无法被消费,这样的消息如果没有后续的处理,就变成了死信,有死信自然就有了死信队列。 应用场景:为了保证订单业务的消息数据不丢失,需要使用到 RabbitMQ 的死信队列机制,档消息消费发生异常时,将消息投入到死信队列中。还有比如说:用户在商城下单成功并点击支付后再指定

dingd1234的博客 1万+

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

RabbitMQ:死信队列

队列中不能被消费的消息称为死信队列有时候因为特殊原因,可能导致队列中的某些信息无法被消费,而队列中这些不能被消费的消息在后期没有进行处理,就会变成死信队列,死信队列中的消息称为死信。应用场景:未来保证订单业务的消息数据不丢失,我们需要使用到RabbitMQ的死信队列机制,当消息消费发生异常的时候,我们就把消息投入到死信队列中,比如说用户买东西,下单成功后去支付,但是没有在指定时间支付的时候就会自动失效。死信队列,英文缩写:DLX。

梦想也许遥不可及,但重要的是追梦的过程,用博客记录自己的成长,记录自己一步一步向上攀登的印记 1万+

《RabbitMQ》什么是死信队列

一 什么是死信队列 当一条消息在队列中出现以下三种情况的时候,该消息就会变成一条死信。 消息被拒绝(basic.reject / basic.nack),并且requeue = false 消息TTL过期 队列达到最大长度 当消息在一个队列中变成一个死信之后,如果配置了死信队列,它将被重新publish到死信交换机,死信交换机将死信投递到一个队列上,这个队列就是死信队列。 二 实现死信队列 2.1 原理图 2.2 创建消费者 创建一个消费者,绑定消费队列及死信交换机,交换机默认为direct模型,死信

Java旅途 9284

RabbitMQ的死信队列

什么是死信 在 RabbitMQ 中充当主角的就是消息,在不同场景下,消息会有不同地表现。 死信就是消息在特定场景下的一种表现形式,这些场景包括: 消息被拒绝访问,即 RabbitMQ返回 nack 的信号时 消息的 TTL 过期时 消息队列达到最大长度 消息不能入队时。 上述场景经常产生死信,即消息在这些场景中时,被称为死信。 什么是死信队列 死信队列就是用于储存死信的消息队列,在死信队列中,有且只有死信构成,不会存在其余类型的消息。 死信队列在 RabbitMQ 中并不会单独存在,往往死信队列都会绑

张三李四王五老六 7102

【RabbitMQ】死信队列

死信(Dead Letter)是消息队列中的一种特殊消息,它指的是那些无法被正常消费或处理的消息.在消息队列系统中,如RabbitMQ,死信队列用于存储这些死信消息. 当消息在一个队列中变成死信之后,它能被重新被发送到另一个交换器中,这个交换器就是DLX(Dead Letter Exchange),绑定DLX的队列,就称为死信队列(Dead Letter Queue,简称DLQ).

2202_75555590的博客 1396

RabbitMQ实现死信队列

RabbitMQ实现死信队列

一恍过去 2万+

Springboot+RabbitMQ死信队列

关于死信队列 在大多数的MQ中间件中,都有死信队列的概念。死信队列同其他的队列一样都是普通的队列。在RabbitMQ中并没有特定的“死信队列”类型,而是通过配置,将其实现。 当我们在创建一个业务的交换机和队列的时候,可以配置参数,指明另一个队列为当前队列的死信队列,在RabbitMQ中,死信队列(严格的说应该是死信交换机)被称为DLX Exchange。当消息“死掉”后,会被自动路由到DLX Ex...

shishishi777的博客 1万+
上一篇: RabbitMQ-TTL机制
下一篇: RabbitMQ-延迟交换器
null_null_bug
博客等级 码龄7年 72粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值