rabbitmq的死信队列

news2024/11/23 21:59:13

目录

成为死信的条件 

消息TTL过期 

 队列达到最大长度

 消息被拒

延迟队列

 延迟队列使用场景

 消息设置 TTL

队列设置 TTL

 两者区别


 

producer 将消息投递到 broker 或者直接到 queue 里了, consumer queue 取出消息
进行消费,但某些时候由于特定的 原因导致 queue 中的某些消息无法被消费 ,这样的消息如果没有后续的处理,就变成了死信,有死信自然就有了死信队列。

成为死信的条件 

  1. 超过消息的存活时间(TTL):可以为消息设置一个存活时间,在该时间段之后,如果消息还未被消费或者被重新投递到其他队列,该消息将成为死信。

  2. 消息被拒绝(Reject):当消息被消费者拒绝接收时,可以选择将该消息重新投递到另一个队列或将其标记为死信。

  3. 消息达到最大重试次数:可以通过在消费者端设置重试次数限制,当消息达到一定的重试次数而仍然无法被消费时,该消息将成为死信。

  4. 队列满溢(Queue Overflow):当一个队列的消息数量已经超过队列的最大容量限制时,新消息无法进入队列,而被视为死信。

 

消息TTL过期 

生产者 

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

public class Producer {
      private static final String NORMAL_EXCHANGE = "normal_exchange";

      public static void main(String[] argv) throws Exception {
      try (Channel channel = RabbitMqUtils.getChannel()) {
      channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
        //设置消息的 TTL 时间
        AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().expiration("10000").build();
        //该信息是用作演示队列个数限制
        for (int i = 1; i <11 ; i++) {
          String message="info"+i;
          channel.basicPublish(NORMAL_EXCHANGE, "zhangsan", properties,
          message.getBytes());
          System.out.println("生产者发送消息:"+message);
        }
      }
   }
}

 消费者1

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

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

public class Consumer01 {
       //普通交换机名称
       private static final String NORMAL_EXCHANGE = "normal_exchange";
       //死信交换机名称
       private static final String DEAD_EXCHANGE = "dead_exchange";
       public static void main(String[] argv) throws Exception {
           Channel channel = RabbitMqUtils.getChannel();
           //声明死信和普通交换机 类型为 direct
           channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
           channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
           //声明死信队列
           String deadQueue = "dead-queue";
           channel.queueDeclare(deadQueue, false, false, false, null);
           //死信队列绑定死信交换机与 routingkey
           channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
           //正常队列绑定死信队列信息
           Map<String, Object> params = new HashMap<>();
           //正常队列设置死信交换机 参数 key 是固定值
           params.put("x-dead-letter-exchange", DEAD_EXCHANGE);
           //正常队列设置死信 routing-key 参数 key 是固定值
           params.put("x-dead-letter-routing-key", "lisi");

           String normalQueue = "normal-queue";
           channel.queueDeclare(normalQueue, false, false, false, params);
           channel.queueBind(normalQueue, NORMAL_EXCHANGE, "zhangsan");
           System.out.println("等待接收消息.....");
           DeliverCallback deliverCallback = (consumerTag, delivery) -> {
              String message = new String(delivery.getBody(), "UTF-8");
              System.out.println("Consumer01 接收到消息"+message);
           };
           channel.basicConsume(normalQueue, true, deliverCallback, consumerTag -> {
           });
       }
}

消费者2

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

public class Consumer02 {
      private static final String DEAD_EXCHANGE = "dead_exchange";
      public static void main(String[] argv) throws Exception {
      Channel channel = RabbitMqUtils.getChannel();
      channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
      String deadQueue = "dead-queue";
      channel.queueDeclare(deadQueue, false, false, false, null);
      channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
      System.out.println("等待接收死信队列消息.....");
      DeliverCallback deliverCallback = (consumerTag, delivery) -> {
         String message = new String(delivery.getBody(), "UTF-8");
         System.out.println("Consumer02 接收死信队列的消息" + message);
      };
      channel.basicConsume(deadQueue, true, deliverCallback, consumerTag -> {
      });
      }
}

关闭消费者1,模拟出故障

正常队列

 消息超时进入死信队列

 死信队列接收信息

 队列达到最大长度

生产者

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

public class Producer {
     private static final String NORMAL_EXCHANGE = "normal_exchange";
     public static void main(String[] argv) throws Exception {
         try (Channel channel = RabbitMqUtils.getChannel()) {
         channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
         //该信息是用作演示队列个数限制
         for (int i = 1; i <11 ; i++) {
         String message="info"+i;
         channel.basicPublish(NORMAL_EXCHANGE,"zhangsan",null, message.getBytes());
         System.out.println("生产者发送消息:"+message);
         }
         }
     }
}

 消费者1

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

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

public class Consumer01 {
       //普通交换机名称
       private static final String NORMAL_EXCHANGE = "normal_exchange";
       //死信交换机名称
       private static final String DEAD_EXCHANGE = "dead_exchange";

       public static void main(String[] argv) throws Exception {
           Channel channel = RabbitMqUtils.getChannel();
           //声明死信和普通交换机 类型为 direct
           channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
           channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
           //声明死信队列
           String deadQueue = "dead-queue";
           channel.queueDeclare(deadQueue, false, false, false, null);
           //死信队列绑定死信交换机与 routingkey
           channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
           //正常队列绑定死信队列信息
           Map<String, Object> params = new HashMap<>();
           //正常队列设置死信交换机 参数 key 是固定值
           params.put("x-dead-letter-exchange", DEAD_EXCHANGE);
           //正常队列设置死信 routing-key 参数 key 是固定值
           params.put("x-dead-letter-routing-key", "lisi");
           // 设置正常队列长度的限制
           params.put("x-max-length",6);

           String normalQueue = "normal-queue";
           channel.queueDeclare(normalQueue, false, false, false, params);
           channel.queueBind(normalQueue, NORMAL_EXCHANGE, "zhangsan");
           System.out.println("等待接收消息.....");
           DeliverCallback deliverCallback = (consumerTag, delivery) -> {
              String message = new String(delivery.getBody(), "UTF-8");
              System.out.println("Consumer01 接收到消息"+message);
           };
           channel.basicConsume(normalQueue, true, deliverCallback, consumerTag -> {
           });
       }
}

 消费者2

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

public class Consumer02 {
      private static final String DEAD_EXCHANGE = "dead_exchange";
      public static void main(String[] argv) throws Exception {
            Channel channel = RabbitMqUtils.getChannel();
            channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
            String deadQueue = "dead-queue";
            channel.queueDeclare(deadQueue, false, false, false, null);
            channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
            System.out.println("等待接收死信队列消息.....");
            DeliverCallback deliverCallback = (consumerTag, delivery) -> {
               String message = new String(delivery.getBody(), "UTF-8");
               System.out.println("Consumer02 接收死信队列的消息" + message);
            };
            channel.basicConsume(deadQueue, true, deliverCallback, consumerTag -> {
            });
      }
}

关闭消费者1,模拟接收不到信息

 死信队列消费了四个消息

 分析:生产者产生10个消息,正常队列只能接受6个消息,多的消息便被转移到死信队列去了

 消息被拒

生产者

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

public class Producer {
     private static final String NORMAL_EXCHANGE = "normal_exchange";
     public static void main(String[] argv) throws Exception {
         try (Channel channel = RabbitMqUtils.getChannel()) {
         channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
         //该信息是用作演示队列个数限制
         for (int i = 1; i <11 ; i++) {
         String message="info"+i;
         channel.basicPublish(NORMAL_EXCHANGE,"zhangsan",null, message.getBytes());
         System.out.println("生产者发送消息:"+message);
         }
         }
     }
}

消费者1


import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

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

public class Consumer01 {
     //普通交换机名称
     private static final String NORMAL_EXCHANGE = "normal_exchange";
     //死信交换机名称
     private static final String DEAD_EXCHANGE = "dead_exchange";
     public static void main(String[] argv) throws Exception {
         Channel channel = RabbitMqUtils.getChannel();
         //声明死信和普通交换机 类型为 direct
         channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
         channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
         //声明死信队列
         String deadQueue = "dead-queue";
         channel.queueDeclare(deadQueue, false, false, false, null);
         //死信队列绑定死信交换机与 routingkey
         channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
         //正常队列绑定死信队列信息
         Map<String, Object> params = new HashMap<>();
         //正常队列设置死信交换机 参数 key 是固定值
         params.put("x-dead-letter-exchange", DEAD_EXCHANGE);
         //正常队列设置死信 routing-key 参数 key 是固定值
         params.put("x-dead-letter-routing-key", "lisi");
         String normalQueue = "normal-queue";
         channel.queueDeclare(normalQueue, false, false, false, params);
         channel.queueBind(normalQueue, NORMAL_EXCHANGE, "zhangsan");
         System.out.println("等待接收消息.....");
         DeliverCallback deliverCallback = (consumerTag, delivery) -> {
             String message = new String(delivery.getBody(), "UTF-8");
             if(message.equals("info5")){
                 System.out.println("Consumer01 接收到消息" + message + "并拒绝签收该消息");
                 //requeue 设置为 false 代表拒绝重新入队 该队列如果配置了死信交换机将发送到死信队列中
                 channel.basicReject(delivery.getEnvelope().getDeliveryTag(), false);
             }else {
                 System.out.println("Consumer01 接收到消息"+message);
                 channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
             }
         };
         boolean autoAck = false;
         channel.basicConsume(normalQueue, autoAck, deliverCallback, consumerTag -> {
         });
     }
}

 消费者2

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

public class Consumer02 {
      private static final String DEAD_EXCHANGE = "dead_exchange";
      public static void main(String[] argv) throws Exception {
            Channel channel = RabbitMqUtils.getChannel();
            channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
            String deadQueue = "dead-queue";
            channel.queueDeclare(deadQueue, false, false, false, null);
            channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
            System.out.println("等待接收死信队列消息.....");
            DeliverCallback deliverCallback = (consumerTag, delivery) -> {
               String message = new String(delivery.getBody(), "UTF-8");
               System.out.println("Consumer02 接收死信队列的消息" + message);
            };
            channel.basicConsume(deadQueue, true, deliverCallback, consumerTag -> {
            });
      }
}

 结果

消费者1,拒绝接受消息info5,info5进入死信队列

 消费者2,死信队列接受到info5

延迟队列

延时队列 , 队列内部是有序的,最重要的特性就体现在它的延时属性上,延时队列中的元素是希望
在指定时间到了以后或之前取出和处理,简单来说,延时队列就是用来存放需要在指定时间被处理的 元素的队列。

 延迟队列使用场景

1. 订单在十分钟之内未支付则自动取消
2. 新创建的店铺,如果在十天内都没有上传过商品,则自动发送消息提醒。
3. 用户注册成功后,如果三天内没有登陆则进行短信提醒。
4. 用户发起退款,如果三天内没有得到处理则通知相关运营人员。
5. 预定会议后,需要在预定的时间点前十分钟通知各个与会人员参加会议

 消息设置 TTL

 

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

public class Producer {
      private static final String NORMAL_EXCHANGE = "normal_exchange";

      public static void main(String[] argv) throws Exception {
      try (Channel channel = RabbitMqUtils.getChannel()) {
      channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
        //设置消息的 TTL 时间
        AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().expiration("10000").build();
        //该信息是用作演示队列个数限制
        for (int i = 1; i <11 ; i++) {
          String message="info"+i;
          channel.basicPublish(NORMAL_EXCHANGE, "zhangsan", properties,
          message.getBytes());
          System.out.println("生产者发送消息:"+message);
        }
      }
   }
}

队列设置 TTL



import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

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

public class Consumer01 {
     //普通交换机名称
     private static final String NORMAL_EXCHANGE = "normal_exchange";
     //死信交换机名称
     private static final String DEAD_EXCHANGE = "dead_exchange";
     public static void main(String[] argv) throws Exception {
         Channel channel = RabbitMqUtils.getChannel();
         //声明死信和普通交换机 类型为 direct
         channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
         channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
         //声明死信队列
         String deadQueue = "dead-queue";
         channel.queueDeclare(deadQueue, false, false, false, null);
         //死信队列绑定死信交换机与 routingkey
         channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
         //正常队列绑定死信队列信息
         Map<String, Object> params = new HashMap<>();
         //正常队列设置死信交换机 参数 key 是固定值
         params.put("x-dead-letter-exchange", DEAD_EXCHANGE);
         //正常队列设置死信 routing-key 参数 key 是固定值
         params.put("x-dead-letter-routing-key", "lisi");
         // 设置 TTL 值为 5000 毫秒(5 秒)
         params.put("x-message-ttl", 5000);
         String normalQueue = "normal-queue";
         channel.queueDeclare(normalQueue, false, false, false, params);
         channel.queueBind(normalQueue, NORMAL_EXCHANGE, "zhangsan");
         System.out.println("等待接收消息.....");
         DeliverCallback deliverCallback = (consumerTag, delivery) -> {
             String message = new String(delivery.getBody(), "UTF-8");
             if(message.equals("info5")){
                 System.out.println("Consumer01 接收到消息" + message + "并拒绝签收该消息");
                 //requeue 设置为 false 代表拒绝重新入队 该队列如果配置了死信交换机将发送到死信队列中
                 channel.basicReject(delivery.getEnvelope().getDeliveryTag(), false);
             }else {
                 System.out.println("Consumer01 接收到消息"+message);
                 channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
             }
         };
         boolean autoAck = false;
         channel.basicConsume(normalQueue, autoAck, deliverCallback, consumerTag -> {
         });
     }
}

 两者区别

如果设置了队列的 TTL 属性,那么一旦消息过期,就会被队列丢弃 ( 如果配置了死信队列被丢到死信队列中) ,而第二种方式,消息即使过期,也不一定会被马上丢弃,因为 消息是否过期是在即将投递到消费者 之前判定的 ,如果当前队列有严重的消息积压情况,则已过期的消息也许还能存活较长时间;另外,还需 要注意的一点是,如果不设置 TTL ,表示消息永远不会过期,如果将 TTL 设置为 0 ,则表示除非此时可以 直接投递该消息到消费者,否则该消息将会被丢弃。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/891474.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

汇聚产学合力二十载,2023英特尔学术大会在南京开幕

8月16日&#xff0c;以“合聚创 共IN智能时代”为主题的“2023英特尔&#xff08;中国&#xff09;学术大会”在南京开幕&#xff0c;邀请专家学者共话科技界前沿趋势&#xff0c;展示科研成果和技术解决方案。本次大会延续了英特尔“为智能而聚能”&#xff0c;推动中国产业界…

ShowMeBug CEO李亚飞受邀出席ArchSummit 全球架构师峰会

2023年7月21-22日&#xff0c;极客邦科技旗下InfoQ中国举办的ArchSummit 全球架构师峰会&#xff08;深圳站&#xff09;2023 在深圳顺利召开。本次会议&#xff0c;聚集了国内外数百位架构师专家来分享技术内容&#xff0c;像MySQL之父、科大讯飞涵盖语言大模型、AIGC、可观测…

214、仿真-基于51单片机温度甲醛一氧化碳(co)电机净化报警Proteus仿真设计(程序+Proteus仿真+配套资料等)

毕设帮助、开题指导、技术解答(有偿)见文未 目录 一、硬件设计 二、设计功能 三、Proteus仿真图 四、程序源码 资料包括&#xff1a; 需要完整的资料可以点击下面的名片加下我&#xff0c;找我要资源压缩包的百度网盘下载地址及提取码。 方案选择 单片机的选择 方案一&a…

Spring系列七:声明式事务

&#x1f418;声明式事务 和AOP有密切的联系, 是AOP的一个实际的应用. &#x1f432;事务分类简述 ●分类 1.编程式事务: 示意代码, 传统方式 Connection connection JdbcUtils.getConnection(); try { //1.先设置事务不要自动提交 connection.setAutoCommit(false…

【数理知识】向量与基的内积,Matlab 代码验证

序号内容1【数理知识】向量的坐标基表示法&#xff0c;Matlab 代码验证2【数理知识】向量与基的内积&#xff0c;Matlab 代码验证 文章目录 1. 向量与基的内积2. 二维平面向量举例3. 代码验证Ref 1. 向量与基的内积 假设存在一个二维平面内的向量 a ⃗ \vec{a} a &#xff0c…

复旦微FR0触摸原理(1)

传统的家电产品通常使用物理按键来进行操作&#xff0c;但随着科技的不断进步&#xff0c;越来越多的家电产品开始采用触摸屏幕和触摸按键来提供更加智能化和便捷的操作方式。 本篇介绍复旦微FM33FR026的触摸检测原理 TSI 模块使用自电容的 方法来检测触摸行为。 自电容检测的原…

半导体退火那些事(1)

1.半导体退火的原理 半导体材料在晶体生长和制造过程中&#xff0c;由于各种原因会出现缺陷、杂质、位错等结构性缺陷&#xff0c;导致晶格不完整&#xff0c;施加电场后的电导率较低。通过退火处理&#xff0c;可以使材料得到修复&#xff0c;结晶体内部重新排列&#xff0c;…

day22 API文档 第一个元素File

file的绝对路径 file的相对路径 file路径的写法 file的各种方法 File.separator 解决虚拟机和windows路径中的斜杠问题 lambda怎么写 file1 file2 file3 file4 public class FileDemo04 {public static void main(String[] args) {File file new File(".");File…

shell连接ubuntu

当使用aws的私钥连接时,老是弹出输入私钥密码,但是根本没有设置过密码,随便输入后,又提示该私钥无密码... 很早就使用过aws的ubuntu,这个问题也很早就遇到过,但是每次遇到都要各种找找找...索性这次记下来算了 此处用FinalShell连接为例 首先现在Putty连接工具: 点击官方下载 …

uploadifive php上传进度条插件 解决动态传参数问题

uploadifive默认只能在加载时,静态传递参数.如果想让用户输入参数.再动态读取.传到后端,是没有直接的办法的 效果图 但我们可以通过settings.formData 来修改配置的方式传参数,完整演示 <form id"file_form{$v.id}" style"display:none"><hr/>…

四川玖璨电商:新媒体短视频运营是做什么?

随着互联网科技的不断发展&#xff0c;新媒体行业如今已经成为了人们获取信息、进行交流的主要渠道之一。在这样的大环境下&#xff0c;短视频成为了新媒体运营的一个重要组成部分。那么&#xff0c;新媒体短视频运营到底是做什么呢&#xff1f;接下来&#xff0c;小编将从几个…

查看 Linux 内核版本的几种方法

uname -a uname -srm uname -r 分拆&#xff1a;Linux 5.13.0-19-generic x86 64 5-内核版本 13-主修订版本 0-19 -次要修订版本 过查看 /proc/version 文件确认 /proc 目录包含虚拟文件&#xff0c;其中包含有关系统内存&#xff0c;CPU内核&#xff0c;已安装文件系统等的信…

【JAVA程序设计】基于SpringBoot+vue的在线考试系统-以计算机网络为例,可自行录入题库-附下载地址

基于SpringBootvue的在线考试系统-以计算机网络为例&#xff0c;可自行录入题库 一、项目简介二、开发环境三、项目技术四、功能结构五、运行截图六、功能实现七、数据库设计八、源码下载地址 一、项目简介 随着信息技术的迅猛发展&#xff0c;教育行业正面临着巨大的变革和挑…

Docker容器安装mysql 8 教程

一、 安装Docker&#xff0c;Docker安装过程如下&#xff1a; 1、卸载系统之前的 docker sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine 2、安装 Docker-CE 安装…

vue利用 sortable 完成表格拖拽

先讲一下vue2&#xff0c;使用sortable完成表格拖拽【不只是表格&#xff0c;div也可以实现&#xff0c;但我项目中是表格拖拽】 github地址 安装 npm install sortablejs --save使用 &#xff08;我的项目中是拖拽一个小按钮移动&#xff0c;而不是整行&#xff09; <te…

字符设备分布注册实现LED灯

目标&#xff1a;通过字符设备的分布注册实现LED灯的控制 字符分布注册&#xff1a; test.c文件 #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #incl…

212、仿真-基于51单片机体温脉搏心率血氧报警Proteus仿真设计(程序+Proteus仿真+配套资料等)

毕设帮助、开题指导、技术解答(有偿)见文未 目录 一、硬件设计 二、设计功能 三、Proteus仿真图 四、程序源码 资料包括&#xff1a; 需要完整的资料可以点击下面的名片加下我&#xff0c;找我要资源压缩包的百度网盘下载地址及提取码。 方案选择 单片机的选择 方案一&a…

快速解决Ubuntu 中 wine 程序 中文显示为方块/显示错误/无法显示中文(2023)

解决办法就是在创建prefix的命令行里加上LANG“zh_CN.UTF8” LC_ALL“zh_CN.UTF8”&#xff0c;并安装cjkfonts&#xff0c;即可。 1、生成prefix、安装cjk字体 以下是基本流程&#xff1a; 现在假定wine和winetricks已经装好&#xff0c; // 先创建一个prefix&#xff0c;按…

切面的魔力:解密Spring AOP 面向切面编程

目录 一、AOP简介 1.1 什么是AOP &#xff1f; 1.2 什么是面向切面编程 &#xff1f; 1.3 AOP 的特点 二、 AOP的基本概念解读 2.1 AOP的基本概念 2.2 AOP 概念趣事解读 三、代码情景演示 3.1 编写目标对象&#xff08;超级英雄们正常的行动&#xff09; 3.2 编写通知…

史上最全HBase面试题,高薪必备,架构必备

说在前面 本文《尼恩 大数据 面试宝典》 是 《尼恩Java面试宝典》姊妹篇。 这里特别说明一下&#xff1a;《尼恩Java面试宝典》41个专题 PDF 自首次发布以来&#xff0c; 已经汇集了 好几千题&#xff0c;大量的大厂面试干货、正货 &#xff0c;足足4800多页&#xff0c;帮助…