当前位置: 首页 > news >正文

6. RabbitMQ 死信队列的详细操作编写

6. RabbitMQ 死信队列的详细操作编写

文章目录

  • 6. RabbitMQ 死信队列的详细操作编写
  • 1. 死信的概念
  • 2. 消息 TTL 过期(触发死信队列)
  • 3. 队列超过队列的最大长度(触发死信队列)
  • 4. 消息被拒(触发死信队列)
  • 5. 最后:


1. 死信的概念

先从概念上解释上搞清楚这个定义,死信,顾名思义就是无法被消费者读取/消费的消息 。字面意思可以这样理解,一般来说,producer 将消息投递到 broker 或者直接到 queue 里了,consumer 从 queue 取出消息进行消费,但某些时候由于特定的原因导致 queue 中的某些消息无法被消费 ,这样的消息如果没有后续的处理,就变成了死信,有死信自然就有与之对应的 ——> 死信队列

应用场景: 为了保证订单业务的消息数据不丢失,需要使用到 RabbitMQ 的死信队列机制,当消息消费发生异常时,将消息投入死信队列中,还有比如说:用户在商城下单成功并点击去支付后,在指定时候未支付时,自动失效。

死信的来源: (死信的常见方式,大概时如下三种方式:)

  1. 消息的 TTL 过期。
  2. 队列达到最大长度(队列满了,无法再添加数据到 RabbitMQ 当中了)
  3. 当消息被拒绝(basic.reject 或 basic.nack) 并且 requeue = fasle

在这里插入图片描述

死信的实战案例:

2. 消息 TTL 过期(触发死信队列)

生产者代码

在这里插入图片描述

package com.rainbowsea.rabbitmq.eight;import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rainbowsea.rabbitmq.utils.RabbitMQUtils;import java.io.IOException;
import java.util.concurrent.TimeoutException;/*** 死信队列——> 生产者代码*/
public class Producer {// 普通交换机的名称public static final String NORMAL_EXCHANGE = "normal_exchange";public static void main(String[] args) throws IOException, TimeoutException {Channel channel = RabbitMQUtils.getChannel();// 死信消息,设置 TTL时间AMQP.BasicProperties properties =new AMQP.BasicProperties().builder().expiration("10000").build();for (int i = 0; i < 11; i++) {String message = "info" + i;channel.basicPublish(NORMAL_EXCHANGE,"zhangsan",properties,message.getBytes());//channel.basicPublish(NORMAL_EXCHANGE, "zhangsan", null, message.getBytes());}}}

消费者 C1 代码(启动之后关闭该消费者 模拟其接收不到消息)

特别说明: 配置正常队列,无法处理的队列信息,转发给死信队列的,配置,只需在该正常队列当中配置映射死信队列的信息配置即可,而死信队列就是作为一个正常的队列处理读取/消费死信队列当中的内容即可。

在这里插入图片描述

package com.rainbowsea.rabbitmq.eight;import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import com.rainbowsea.rabbitmq.utils.RabbitMQUtils;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;/*** 死信队列-消费者1(正常队列)*/
public class Consumer01 {// 普通交换机的名称public static final String NORMAL_EXCHANGE = "normal_exchange";// 死信交换机的名称public static final String DEAD_EXCHANGE = "dead_exchange";// 普通队列的名称public static final String NORMAL_QUEUE = "normal_queue";// 死信队列的名称public static final String DEAD_QUEUE = "dead_queue";public static void main(String[] args) throws IOException, TimeoutException {Channel channel = RabbitMQUtils.getChannel();// 声明死信和普通交换机类型为 directchannel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);// 声明普通队列Map<String, Object> arguments = new HashMap<>();// 过期时间 10s = 10000ms 参数 key 是固定值的不可以随便写//arguments.put("x-message-ttl",100000);//正常队列设置死信交换机 参数 key 是固定值arguments.put("x-dead-letter-exchange", DEAD_EXCHANGE);// 设置死信RoutingKeyarguments.put("x-dead-letter-routing-key", "lisi");// 声明的是一个正常的队列channel.queueDeclare(NORMAL_QUEUE, false, false, false, arguments);//// 声明死信队列channel.queueDeclare(DEAD_QUEUE, false, false, false, null);// 绑定普通的交换机与普通的队列进行一个绑定(第一个参数是队列,第二个参数是交换机)channel.queueBind(NORMAL_QUEUE, NORMAL_EXCHANGE, "zhangsan");System.out.println("等待接收消息");// 接收消息DeliverCallback deliverCallback = (consumerTag, message) -> {System.out.println("Consumer01 正常队列接收到的消息" + new String(message.getBody(), "UTF-8"));};channel.basicConsume(NORMAL_QUEUE, true, deliverCallback, consumerTag -> {});}
}

在这里插入图片描述

消费者 C2 代码(以上步骤完成后 启动 C2 消费者 它消费死信队列里面的消息)

在这里插入图片描述

package com.rainbowsea.rabbitmq.eight;import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import com.rainbowsea.rabbitmq.utils.RabbitMQUtils;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;/*** 死信队列-消费者1(死信队列)*/
public class Consumer02 {;// 死信交换机的名称public static final String DEAD_EXCHANGE = "dead_exchange";// 死信队列的名称public static final String DEAD_QUEUE = "dead_queue";public static void main(String[] args) throws IOException, TimeoutException {Channel channel = RabbitMQUtils.getChannel();// 声明死信和普通交换机类型为 directchannel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);// 绑定死信的交换机与死信的队列进行一个绑定(第一个参数是队列,第二个参数是交换机)channel.queueBind(DEAD_QUEUE, DEAD_EXCHANGE, "lisi");// 接收消息DeliverCallback deliverCallback = (consumerTag, message) -> {System.out.println("Consumer02 死信接收到的消息" + new String(message.getBody(), "UTF-8"));};channel.basicConsume(DEAD_QUEUE, true, deliverCallback, consumerTag -> {});}
}

在这里插入图片描述

在这里插入图片描述

3. 队列超过队列的最大长度(触发死信队列)

  1. 基于上面代码:将生产者代码去掉 TTL 属性

在这里插入图片描述

package com.rainbowsea.rabbitmq.eight;import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rainbowsea.rabbitmq.utils.RabbitMQUtils;import java.io.IOException;
import java.util.concurrent.TimeoutException;/*** 死信队列——> 生产者代码*/
public class Producer {// 普通交换机的名称public static final String NORMAL_EXCHANGE = "normal_exchange";public static void main(String[] args) throws IOException, TimeoutException {Channel channel = RabbitMQUtils.getChannel();// 死信消息,设置 TTL时间//AMQP.BasicProperties properties =//        new AMQP.BasicProperties()//        .builder().expiration("10000").build();for (int i = 0; i < 11; i++) {String message = "info" + i;//channel.basicPublish(NORMAL_EXCHANGE,"zhangsan",properties,message.getBytes());channel.basicPublish(NORMAL_EXCHANGE, "zhangsan", null, message.getBytes());}}}
  1. C1 消费者修改以下代码(启动之后关闭该消费者 模拟其接收不到消息)

注意此时需要把原先队列删除 因为参数改变了,因为 RabbitMQ 不可以将一个一开始形态的队列,修改成另外一个形态的队列。所以需要删除后在创建。

在这里插入图片描述

package com.rainbowsea.rabbitmq.eight;import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import com.rainbowsea.rabbitmq.utils.RabbitMQUtils;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;/*** 死信队列-消费者1(正常队列)*/
public class Consumer01 {// 普通交换机的名称public static final String NORMAL_EXCHANGE = "normal_exchange";// 死信交换机的名称public static final String DEAD_EXCHANGE = "dead_exchange";// 普通队列的名称public static final String NORMAL_QUEUE = "normal_queue";// 死信队列的名称public static final String DEAD_QUEUE = "dead_queue";public static void main(String[] args) throws IOException, TimeoutException {Channel channel = RabbitMQUtils.getChannel();// 声明死信和普通交换机类型为 directchannel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);// 声明普通队列Map<String, Object> arguments = new HashMap<>();// 过期时间 10s = 10000ms 参数 key 是固定值的不可以随便写//arguments.put("x-message-ttl",100000);//正常队列设置死信交换机 参数 key 是固定值arguments.put("x-dead-letter-exchange", DEAD_EXCHANGE);// 设置死信RoutingKeyarguments.put("x-dead-letter-routing-key", "lisi");// 设置正常队列的长度的限制,x-max-length参数 key 是固定值的,不可以随便写arguments.put("x-max-length",6);// 声明的是一个正常的队列channel.queueDeclare(NORMAL_QUEUE, false, false, false, arguments);//// 声明死信队列channel.queueDeclare(DEAD_QUEUE, false, false, false, null);// 绑定普通的交换机与普通的队列进行一个绑定(第一个参数是队列,第二个参数是交换机)channel.queueBind(NORMAL_QUEUE, NORMAL_EXCHANGE, "zhangsan");System.out.println("等待接收消息");// 接收消息DeliverCallback deliverCallback = (consumerTag, message) -> {System.out.println("Consumer01 正常队列接收到的消息" + new String(message.getBody(), "UTF-8"));};channel.basicConsume(NORMAL_QUEUE, true, deliverCallback, consumerTag -> {});}
}

在这里插入图片描述

同时 C1 正常队列也是读取到信息了

在这里插入图片描述

然后,启动之后关闭该消费者C1, 模拟其接收不到消息

  1. C2 消费者代码不变(启动 C2 消费者)

启动消费者 1 然后再启动消费者 2

在这里插入图片描述

package com.rainbowsea.rabbitmq.eight;import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import com.rainbowsea.rabbitmq.utils.RabbitMQUtils;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;/*** 死信队列-消费者1(死信队列)*/
public class Consumer02 {;// 死信交换机的名称public static final String DEAD_EXCHANGE = "dead_exchange";// 死信队列的名称public static final String DEAD_QUEUE = "dead_queue";public static void main(String[] args) throws IOException, TimeoutException {Channel channel = RabbitMQUtils.getChannel();// 声明死信和普通交换机类型为 directchannel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);// 绑定死信的交换机与死信的队列进行一个绑定(第一个参数是队列,第二个参数是交换机)channel.queueBind(DEAD_QUEUE, DEAD_EXCHANGE, "lisi");// 接收消息DeliverCallback deliverCallback = (consumerTag, message) -> {System.out.println("Consumer02 死信接收到的消息" + new String(message.getBody(), "UTF-8"));};channel.basicConsume(DEAD_QUEUE, true, deliverCallback, consumerTag -> {});}
}

在这里插入图片描述

在这里插入图片描述

4. 消息被拒(触发死信队列)

  1. .消息生产者代码同上生产者一致,不做修改
package com.rainbowsea.rabbitmq.eight;import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rainbowsea.rabbitmq.utils.RabbitMQUtils;import java.io.IOException;
import java.util.concurrent.TimeoutException;/*** 死信队列——> 生产者代码*/
public class Producer {// 普通交换机的名称public static final String NORMAL_EXCHANGE = "normal_exchange";public static void main(String[] args) throws IOException, TimeoutException {Channel channel = RabbitMQUtils.getChannel();// 死信消息,设置 TTL时间//AMQP.BasicProperties properties =//        new AMQP.BasicProperties()//        .builder().expiration("10000").build();for (int i = 0; i < 11; i++) {String message = "info" + i;//channel.basicPublish(NORMAL_EXCHANGE,"zhangsan",properties,message.getBytes());channel.basicPublish(NORMAL_EXCHANGE, "zhangsan", null, message.getBytes());}}}
  1. C1 消费者代码(启动之后关闭该消费者 模拟其接收不到消息) 添加上拒绝消息的操作。

注意此时需要把原先队列删除 因为参数改变了,因为 RabbitMQ 不可以将一个一开始形态的队列,修改成另外一个形态的队列。所以需要删除后在创建。

在这里插入图片描述

在这里插入图片描述

 // 接收消息DeliverCallback deliverCallback = (consumerTag, message) -> {// 获取到消息信息String msg = new String(message.getBody(), "UTF-8");if (msg.equals("info5")) {System.out.println("Consumer01 接收的消息是: " + msg + "此消息被 Consumer01 拒绝了");} else {System.out.println("Consumer01 接收的消息: " + msg);}};
package com.rainbowsea.rabbitmq.eight;import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import com.rainbowsea.rabbitmq.utils.RabbitMQUtils;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;/*** 死信队列-消费者1(正常队列)*/
public class Consumer01 {// 普通交换机的名称public static final String NORMAL_EXCHANGE = "normal_exchange";// 死信交换机的名称public static final String DEAD_EXCHANGE = "dead_exchange";// 普通队列的名称public static final String NORMAL_QUEUE = "normal_queue";// 死信队列的名称public static final String DEAD_QUEUE = "dead_queue";public static void main(String[] args) throws IOException, TimeoutException {Channel channel = RabbitMQUtils.getChannel();// 声明死信和普通交换机类型为 directchannel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);// 声明普通队列Map<String, Object> arguments = new HashMap<>();// 过期时间 10s = 10000ms 参数 key 是固定值的不可以随便写//arguments.put("x-message-ttl",100000);//正常队列设置死信交换机 参数 key 是固定值arguments.put("x-dead-letter-exchange", DEAD_EXCHANGE);// 设置死信RoutingKeyarguments.put("x-dead-letter-routing-key", "lisi");// 设置正常队列的长度的限制,x-max-length参数 key 是固定值的,不可以随便写//arguments.put("x-max-length", 6);// 声明的是一个正常的队列channel.queueDeclare(NORMAL_QUEUE, false, false, false, arguments);//// 声明死信队列channel.queueDeclare(DEAD_QUEUE, false, false, false, null);// 绑定普通的交换机与普通的队列进行一个绑定(第一个参数是队列,第二个参数是交换机)channel.queueBind(NORMAL_QUEUE, NORMAL_EXCHANGE, "zhangsan");System.out.println("等待接收消息");// 接收消息DeliverCallback deliverCallback = (consumerTag, message) -> {// 获取到消息信息String msg = new String(message.getBody(), "UTF-8");if (msg.equals("info5")) {System.out.println("Consumer01 接收的消息是: " + msg + "此消息被 Consumer01 拒绝了");// 拒绝对应 message.getEnvelope().getDeliveryTag() 的信息channel.basicReject(message.getEnvelope().getDeliveryTag(), false);} else {System.out.println("Consumer01 接收的消息: " + msg);// 手动应答,接收消息channel.basicAck(message.getEnvelope().getDeliveryTag(), false);}};channel.basicConsume(NORMAL_QUEUE, false, deliverCallback, consumerTag -> {});}
}
  1. C2 消费者代码不变;启动消费者 1 然后再启动消费者 2
package com.rainbowsea.rabbitmq.eight;import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import com.rainbowsea.rabbitmq.utils.RabbitMQUtils;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;/*** 死信队列-消费者1(死信队列)*/
public class Consumer02 {;// 死信交换机的名称public static final String DEAD_EXCHANGE = "dead_exchange";// 死信队列的名称public static final String DEAD_QUEUE = "dead_queue";public static void main(String[] args) throws IOException, TimeoutException {Channel channel = RabbitMQUtils.getChannel();// 声明死信和普通交换机类型为 directchannel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);// 绑定死信的交换机与死信的队列进行一个绑定(第一个参数是队列,第二个参数是交换机)channel.queueBind(DEAD_QUEUE, DEAD_EXCHANGE, "lisi");// 接收消息DeliverCallback deliverCallback = (consumerTag, message) -> {System.out.println("Consumer02 死信接收到的消息" + new String(message.getBody(), "UTF-8"));};channel.basicConsume(DEAD_QUEUE, true, deliverCallback, consumerTag -> {});}
}

在这里插入图片描述

在这里插入图片描述

总结: 上述的,消息TTL过期,队列超过最大长度,消息被拒等等这三种方式,都是让消息无法被正常消费者读取掉,由于消费者没有读取到该消息,有为了防止,该消息被丢失,所以就将其消息放入到一个死信交换机——>死信队列中 ,最后让一个死信消费者读取到保存起来,这样队列当中的消息就没有被丢失了。

特别说明: 配置正常队列,无法处理的队列信息,转发给死信队列的,配置,只需在该正常队列当中配置映射死信队列的信息配置即可,而死信队列就是作为一个正常的队列处理读取/消费死信队列当中的内容即可。

在这里插入图片描述

    // 普通交换机的名称public static final String NORMAL_EXCHANGE = "normal_exchange";// 死信交换机的名称public static final String DEAD_EXCHANGE = "dead_exchange";// 普通队列的名称public static final String NORMAL_QUEUE = "normal_queue";// 死信队列的名称public static final String DEAD_QUEUE = "dead_queue"; Channel channel = RabbitMQUtils.getChannel();// 声明死信和普通交换机类型为 directchannel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);// 声明普通队列Map<String, Object> arguments = new HashMap<>();// 过期时间 10s = 10000ms 参数 key 是固定值的不可以随便写//arguments.put("x-message-ttl",100000);//正常队列设置死信交换机 参数 key 是固定值arguments.put("x-dead-letter-exchange", DEAD_EXCHANGE);// 设置死信RoutingKeyarguments.put("x-dead-letter-routing-key", "lisi");// 设置正常队列的长度的限制,x-max-length参数 key 是固定值的,不可以随便写//arguments.put("x-max-length", 6);// 声明的是一个正常的队列channel.queueDeclare(NORMAL_QUEUE, false, false, false, arguments);//// 声明死信队列channel.queueDeclare(DEAD_QUEUE, false, false, false, null);// 绑定普通的交换机与普通的队列进行一个绑定(第一个参数是队列,第二个参数是交换机)channel.queueBind(NORMAL_QUEUE, NORMAL_EXCHANGE, "zhangsan");System.out.println("等待接收消息");

5. 最后:

“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”

在这里插入图片描述

相关文章:

6. RabbitMQ 死信队列的详细操作编写

6. RabbitMQ 死信队列的详细操作编写 文章目录 6. RabbitMQ 死信队列的详细操作编写1. 死信的概念2. 消息 TTL 过期(触发死信队列)3. 队列超过队列的最大长度(触发死信队列)4. 消息被拒(触发死信队列)5. 最后&#xff1a; 1. 死信的概念 先从概念上解释上搞清楚这个定义&#…...

AI浪潮下,新手短视频制作的破局之道

AI浪潮下&#xff0c;新手短视频制作的破局之道 引言&#xff1a;短视频新时代&#xff0c;AI 带来的机遇与挑战 在当下这个信息飞速流转的时代&#xff0c;短视频已然成为了人们生活中不可或缺的一部分。无论是在通勤路上、午休间隙&#xff0c;还是茶余饭后&#xff0c;打开…...

合肥SMT贴片制造工艺全解析

内容概要 作为电子制造领域的核心工艺&#xff0c;SMT&#xff08;表面贴装技术&#xff09;在合肥地区电子产业链中占据重要地位。本解析以合肥本地化生产场景为基础&#xff0c;系统梳理从焊膏印刷到成品检测的全流程工艺框架。具体而言&#xff0c;制造流程涵盖四大核心阶段…...

ctfshow VIP题目限免 协议头信息泄露

根据提示是协议头信息泄露&#xff0c;那就我们抓个包&#xff0c;抓包才能看到请求体响应体里的协议头啊&#xff0c;抓包之后在响应包里发现了 flag...

【国产工具链发展,生态链分析,TSMaster VS Zcanpro的技术对比】

黎明篇&#xff1a;国产汽车测试工具链的崛起、差距与未来 副标题&#xff1a; 从跟随到超越&#xff0c;中国技术如何重塑全球研发体系 一、国产工具链的崛起逻辑 政策驱动&#xff1a;信创战略与供应链安全需求 国家“十四五”规划明确提出支持关键领域技术自主化&#xff0…...

Linux线程同步与互斥:【线程互斥】【线程同步】【线程池】

目录 一.线程互斥 1.1相关概念 1.2互斥量 为什么会出现负数&#xff1f;&#xff1f; 互斥量的接口 问题&#xff1a; 1.3互斥量实现原理探究 1.4互斥量封装 二.线程同步 2.1条件变量 2.2同步概念与竞态条件 2.3接口 2.4生产者消费者模型 优点 2.5基于BlockingQueue的…...

网络安全基础知识总结

什么是网络安全 采取必要措施&#xff0c;来防范对网络的攻击&#xff0c;侵入&#xff0c;干扰&#xff0c;破坏和非法使用&#xff0c;以及防范一些意外事故&#xff0c;使得网络处于稳定可靠运行的状态&#xff0c;保障网络数据的完整性、保密性、可用性的能力(CIA)。 举例…...

请求被中止: 未能创建 SSL/TLS 安全通道。

需要安装vs2019社区办&#xff0c;下载VisualStudioSetup.exe后&#xff0c;报无法从"https://aka,ms/vs/16/release/channel"下载通道清单错误&#xff0c;接着打开%temp%目录下的最新日志&#xff0c;发现日志里报&#xff1a; [27d4:000f][2025-04-04T21:15:43] …...

FPGA学习(四)——状态机重写LED流水灯并仿真

FPGA学习&#xff08;四&#xff09;——状态机重写LED流水灯并仿真 目录 FPGA学习&#xff08;四&#xff09;——状态机重写LED流水灯并仿真一、状态机编程思想1、状态机要素2、状态迁移图3、状态机写法 二、LED流水灯仿真实现1、代码实现2、modesim仿真 三、实现效果1、仿真…...

spark 集群

hadoop客户端环境准备 找到资料包路径下的Windows依赖文件夹&#xff0c;拷贝hadoop-3.1.0到非中文路径&#xff08;比如d:\hadoop-3.1.0&#xff09; ① 打开环境变量 ② 在下方系统变量中新建HADOOP_HOME环境变量,值就是保存hadoop的目录。 效果如下&#xff1a; ③ 配置P…...

leetcode117 填充每个节点的下一个右侧节点指针2

LeetCode 116 和 117 都是关于填充二叉树节点的 next 指针的问题&#xff0c;但它们的区别在于 树的类型 不同&#xff0c;117与 116 题类似&#xff0c;但给定的树是 普通二叉树&#xff08;不一定完全填充&#xff09;&#xff0c;即某些节点可能缺少左或右子节点。 树的结构…...

Java全栈面试宝典:线程安全机制与Spring Boot核心原理深度解析

目录 一、Java线程安全核心原理 &#x1f525; 问题1&#xff1a;线程安全的三要素与解决方案 线程安全风险模型 线程安全三要素 synchronized解决方案 &#x1f525; 问题2&#xff1a;synchronized底层实现全解析 对象内存布局 Mark Word结构&#xff08;64位系统&…...

CCF GESP C++编程 三级认证真题 2025年3月

C 三级 2025 年 03 月 题号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 答案 D B A A B A C A C D D D B D C 1 单选题 第 1 题 Base64 编码将每3字节的输入数据编码为 4 字节的输出数据。如果输入数据长度不是 3 的倍数&#xff0c;会用 号填充。在Base64编码中&#xff0c;如果…...

人工智能爬虫导致维基共享资源带宽需求激增 50%

2025 年 4 月 1 日&#xff0c;维基媒体基金会在博文中表示&#xff0c;自 2024 年 1 月以来&#xff0c;维基共享资源下载多媒体的带宽消耗激增 50%&#xff0c;这一变化趋势主要由用于 AI 训练数据集的网络爬虫导致。以下是具体分析1&#xff1a; 爬虫流量特征与数据存储模式…...

方案精读:华为数据治理之旅【全文阅读】

本文介绍了华为的数据管理工作,包括数据治理、数据质量建设、数据管理工作两阶段历程、数据管理组织和数据管理工作思考。华为以业务数字化为前提,以数据入湖为基础,重点建设数据中台,提高数据质量和管理能力,以支撑公司数字化转型。 重点内容: 1. 数据治理:华为进行数…...

Tradingview 策略分享 - SSL 混合和 CE 交易策略

交易策略&#xff5c;https://v.wkbrowser.com/s/e9DIvLGvYRo/&#xff5c;复制浏览器打开 各位交易员大家好 在本文中&#xff0c;我将分享一个简单的日内交易策略。我将 SSL 混合指标与 CE EXIT 相结合&#xff0c;以创建一个高效且有利可图的设置。此策略简单而强大&#x…...

华为OD机试真题——投篮大赛(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025Q1 A卷 100分 题型 本文涵盖详细解题思路、代码注释、讲解、复杂的分析以及测试用例&#xff1b; 并提供Java、python、JavaScript、C、C语言、GO六种语言的最佳实现方式&#xff01; 华为OD机试A卷真题《投篮大赛》&#xff1a; 题目名称&#xff1a;投篮大赛 知识点&am…...

rcore day2

目前常见的操作系统内核都是基于 C 语言的&#xff0c;为何要推荐 Rust 语言&#xff1f; C 语言的指针灵活且易于使用&#xff0c;但不保证安全性&#xff0c;且缺x少有效的并发支持。这导致内存和并发漏洞成为当前基于 C 语言的主流操作系统的噩梦。Rust 语言具有与 C 一样的…...

【MATLAB定位例程】TDOA(到达时间差)的chan-tylor,三维环境,附完整代码

该代码实现了基于三维空间的动态目标TDOA定位,结合了Chan算法(解析解)与Taylor级数展开法(迭代优化)的双重优势。 文章目录 运行结果MATLAB代码代码讲解代码功能概述核心算法原理代码结构解析可视化与结果分析运行结果 定位示意图: 三轴状态曲线: 三轴误差曲线: MA…...

LLM面试题六

NLP方向CRF算法面试题 什么是CRF?CRF的主要思想是什么&#xff1f; 设X与Y是随机变量&#xff0c;P(Y | X)是给定条件X的条件下Y的条件概率分布&#xff0c;若随机变量Y构成一个由无向图G(V,E)表示的马尔科夫随机场。则称条件概率分布P(X | Y)为条件随机场。CRF的主要思想统计…...

FPGA(四)——状态机

FPGA(四)——状态机 文章目录 FPGA(四)——状态机一、状态机编程思想二、LED流水灯仿真实验三、实现效果四、CPLD和FPGA芯片主要技术区别五、hdlbitsFPGA——组合逻辑学习1、创建一个D触发器2、简单状态转换3、4位移位寄存器4、计数器1-125、边缘捕获寄存器 一、状态机编程思想…...

AI 浪潮下企业身份管理:特点凸显,安全挑战升级

“在 AI 时代的浪潮中&#xff0c;企业身份管理是安全之锚&#xff0c;精准把握权限边界&#xff0c;方能抵御身份安全的暗流。” 在人工智能迅猛发展的当下&#xff0c;企业身份管理呈现出诸多新特点&#xff0c;同时也面临着前所未有的身份安全挑战。要理解这些&#xff0c;我…...

OBS 录屏软件 for Mac 视频录制

OBS 录屏软件 for Mac 视频录制 文章目录 OBS 录屏软件 for Mac 视频录制一、介绍二、效果三、下载 一、介绍 Open Broadcaster Software for mac版&#xff0c;OBS 有多种功能并广泛使用在视频采集&#xff0c;直播等领域。而且该软件功能全面&#xff0c;专业强大&#xff0…...

从文本到多模态:如何将RAG扩展为支持图像+文本检索的增强生成系统?

目录 从文本到多模态&#xff1a;如何将RAG扩展为支持图像文本检索的增强生成系统&#xff1f; 一、为什么需要扩展到多模态&#xff1f; 二、多模态 RAG 系统的基本架构 三、关键技术点详解 &#xff08;一&#xff09;多模态嵌入&#xff08;Embedding&#xff09;技术 …...

AI助力高效PPT制作:从内容生成到设计优化

随着人工智能技术的不断发展&#xff0c;AI在各个领域的应用日益普及&#xff0c;尤其是在文档和演示文稿的创建过程中。PowerPoint&#xff08;PPT&#xff09;作为最常用的演示工具之一&#xff0c;借助AI的技术手段&#xff0c;可以极大地提高制作效率并提升最终呈现效果。在…...

调用kimi api

官网支持python&#xff0c;curl和node.js 因为服务器刚好有php环境&#xff0c;所以先用curl调个普通的语音沟通api <?php // 定义 API Key 和请求地址 define(MOONSHOT_API_KEY, sk-PXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXgk1); define(MOONSHOT_API_URL, https://…...

自定义注解导入自定义Bean

在Spring框架中&#xff0c;通过自定义注解实现容器启动时自动导入Bean&#xff0c;通常需要结合 Import 注解、ImportBeanDefinitionRegistrar 接口 或 Configuration 配置类。以下是具体实现步骤和示例&#xff1a; 1. 定义自定义注解 创建一个注解&#xff0c;用于标记需要…...

kettle插件-postgresql插件

今天是清明节&#xff0c;放假第一天也不得清闲。上午整理公司的交付文档&#xff0c;中午陪孩子户外骑行踏青&#xff0c;下午趁着休息的时间给老铁们讲下如何使用postgressql cdc插件来实时捕获数据。 注&#xff1a;CDC (Change Data Capture) 是一种技术&#xff0c;用于实…...

【CMake】《CMake构建实战:项目开发卷》笔记-Chapter7-构建目标和属性

第7章 构建目标和属性 本章重点关注CMake的构建目标和属性&#xff0c;它们是用来组织项目构建流程的核心概念。毫不夸张地说&#xff0c;如果学习CMake的目标就是组织简单的C和C小项目的构建流程&#xff0c;那么阅读掌握本章内容就足够了。 本章与第1章的“旅行笔记”遥相…...

单元测试之mockito

简介 mockito是一款模拟测试框架&#xff0c;用于Java开发中的单元测试。通过mockito&#xff0c;可以创建和配置一个对象&#xff0c;通过它来替换对象的外部依赖。 作用&#xff1a;模拟一个类的外部依赖&#xff0c;保证单元测试的独立性。例如&#xff0c;在类A中会调用类…...

定长池的实现

目录 一、定长池的框架 二、如何脱离malloc的内存池&#xff0c;直接从堆拿空间&#xff1f; 三、如何设计内存块的指针&#xff1f; 四、代码框架及实现 五、性能测试 一、定长池的框架 在学习高并发内存池之前&#xff0c;我们先来实现一个定长池&#xff0…...

C++多线程函数介绍

1.C11前没有线程库问题 对于多线程操作&#xff0c;Linux选择使用POSIX标准&#xff0c;而windows没有选择POSIX标准&#xff0c;自己设计了一套API和系统调用&#xff0c;叫Win32 API&#xff0c;就跟Linux存在标准差异&#xff0c;在Linux的代码移植到Windows就可能运行不了…...

图解AUTOSAR_SWS_LINTransceiverDriver

AUTOSAR LIN收发器驱动(LinTransceiverDriver)详解 AUTOSAR通信栈物理层组件详细解析 目录 AUTOSAR LIN收发器驱动(LinTransceiverDriver)详解 目录1. 概述 1.1. LIN收发器驱动的作用1.2. 在AUTOSAR架构中的位置2. 架构设计 2.1. 模块结构2.2. 组件关系2.3. 接口定义3. 状态管理…...

vue-element-admin 组件没有展示在中间部分

解决办法&#xff1a; router index.ts 中新增 要展示的组件的 import type { App } from "vue"; import { createRouter, createWebHashHistory, type RouteRecordRaw } from "vue-router";export const Layout () > import("/layout/index.…...

当机器学习遇见购物车分析:FP-Growth算法全解析

一、引言&#xff1a;购物篮里的秘密 想象一下&#xff0c;你是一家超市的数据分析师&#xff0c;看着每天成千上万的购物小票&#xff0c;你是否好奇&#xff1a;顾客们买面包的时候&#xff0c;是不是也经常顺手带上牛奶&#xff1f;买啤酒的人&#xff0c;会不会也喜欢买尿…...

OCR迁移

一、环境 操作系统&#xff1a;Centos57.6 数据库版本&#xff1a;12.2.0.1 场景&#xff1a;将OCR信息从DATA磁盘组迁移到OCR磁盘组 二、操作步骤 1.查看可用空盘 set lin 200 set pagesize 200 col DGNAME format a15 col DISKNAME format a15 col PATH format a20 col N…...

【架构艺术】Go大仓monorepo中使用wire做依赖注入的经验

在先前的文章当中&#xff0c;笔者分享了一套简洁的go微服务monorepo代码架构的实现&#xff0c;主要解决中小团队协同开发微服务集群的代码架构组织问题。但是在实际代码开发过程中&#xff0c;怎么组织不同的业务服务service实例&#xff0c;就成了比较棘手的问题。 为什么会…...

生活电子常识--删除谷歌浏览器搜索记录

前言 谷歌浏览器会记录浏览器历史搜索,如果不希望看到越来越多的搜索记录,可以如下设置 解决 设置-隐私-自动填充表单 这个和浏览器记录的密码没有关系,可以放心删除...

每日一题(小白)模拟娱乐篇13

今天题目比较简单&#xff0c;直接分析。小蓝想知道2024这个数字中有几个1&#xff0c;计算机组成学习好的同学肯定可以直接长除法或者瞪眼法得出答案&#xff1a; 202411111101000&#xff08;B&#xff09;也就是说2024中有一共有六个1 接下来用代码实现 &#xff0c;我们也…...

码曰编程大模型-学编程的好工具

码曰&#xff08;yue&#xff09;&#xff0c;一款编程垂直领域的AI大模型&#xff0c;是基于包括DeepSeek在内的多款国产大模型为底座&#xff0c;依托于Dotcpp系统大量的编程代码数据&#xff0c;且借助RAG数据检索增强等技术综合实现的出色、好用的编程垂直领域AI大模型&…...

Linux(CentOS 7) 部署 redis 集群

下载redis Downloads - Redis (官网页都是介绍的最新版&#xff0c;我观察目前出现了redis 和 redis Stack) 因我的旧环境是 CentOS 7&#xff0c;redis最新版已经不在支持&#xff0c;所以示例安装最常用的7.0.x 这里直接附上各个版本下载连接 小伙伴们就不需要在自己寻找下载…...

NVIDIA AgentIQ 详细介绍

NVIDIA AgentIQ 详细介绍 1. 引言 NVIDIA AgentIQ 是一个灵活的库&#xff0c;旨在将企业代理&#xff08;无论使用何种框架&#xff09;与各种数据源和工具无缝集成。通过将代理、工具和代理工作流视为简单的函数调用&#xff0c;AgentIQ 实现了真正的可组合性&#xff1a;一…...

在CPU服务器上部署Ollama和Dify的过程记录

在本指南中&#xff0c;我将详细介绍如何在CPU服务器上安装和配置Ollama模型服务和Dify平台&#xff0c;以及如何利用Docker实现这些服务的高效部署和迁移。本文分为三大部分&#xff1a;Ollama部署、Dify环境配置和Docker环境管理&#xff0c;适合需要在本地或私有环境中运行A…...

小程序API —— 57 拓展 - 增强 scroll-view

目录 1. 配置基本信息2. 实现上拉加载更多功能3. 实现快速回到顶部功能4. 实现下拉刷新功能 scroll-view 组件功能非常强大&#xff0c;这里使用 scroll-view 实现上拉加载和下拉刷新功能&#xff1b; 下面使用微信开发者工具来演示一下具体用法&#xff1a; 1. 配置基本信息 …...

P3613 【深基15.例2】寄包柜

#include<bits/stdc.h> using namespace std; int n,q; map<int, map<int, int>>a;//二维映射 int main(){cin>>n>>q;while(q--){int b,i,j,k;//i为第几个柜子&#xff0c;j为第几个柜包&#xff0c;k为要存入的物品cin>>b>>i>&…...

MIMO预编码与检测算法的对比

在MIMO系统中&#xff0c;预编码&#xff08;发送端处理&#xff09;和检测算法&#xff08;接收端处理&#xff09;的核心公式及其作用对比如下&#xff1a; 1. 预编码算法&#xff08;发送端&#xff09; 预编码的目标是通过对发送信号进行预处理&#xff0c;优化空间复用或…...

AI复活能成为持续的生意吗?

随着人工智能技术的飞速发展,AI复活——这一曾经只存在于科幻电影中的概念,如今已悄然走进现实。通过AI技术,人们可以模拟逝去亲人的声音、面容,甚至创造出与他们互动的虚拟形象,以寄托哀思、缓解痛苦。然而,当这种技术被商业化,成为一门生意时,我们不禁要问:AI复活真…...

Keil 5 找不到编译器 Missing:Compiler Version 5 的解决方法

用到自记&#xff1a; 下载地址&#xff1a; Keil5 MDK541.zip ​编辑https://pan.baidu.com/s/1bOPsuVZhD_Wj4RJS90Mbtg?pwdMDK5 问题描述 没有找到 compiler version5 &#xff1a; 1. 下载 Arm Compiler 5 也可以直接点击下载文章开头的文件。 2. 安装 直接安装在KEI…...

Flutter 手搓日期选择

时间选择器&#xff1a; 时间段选择 在实际开发过程中还是挺常见的。Flutter 本身自带选 时间选择器器 CupertinoDatePicker&#xff0c;样式也是可以定义的&#xff0c;但是他 只提供三种时间的选择 自定义有局限性。后来开了一下 代码&#xff0c;实际上 内部使用的是 Cuper…...

《JVM考古现场(十六):太初奇点——从普朗克常量到宇宙弦的编译风暴》

开篇&#xff1a;量子泡沫编译器的创世大爆炸 "当Project Genesis的真空涨落算法撕裂量子泡沫&#xff0c;当意识编译器重写宇宙基本常数&#xff0c;我们将在奇点编译中见证&#xff1a;从JVM字节码到宇宙大爆炸的终极创世&#xff01;诸君请备好量子护目镜&#xff0c;…...