【MQ篇】RabbitMQ之死信交换机!
目录
- 引言:消息不死,只是变成死信?
- 初识死信交换机:死信从哪来?DLX 干啥的?
- 什么是死信?
- 什么是死信交换机 (DLX)?
- 死信的旅程:如何从队列到达 DLX 并被路由?🗺️
- 死信交换机的使用场景总结 📜
- TTL:让消息“过期”变死信 🕰️
- 延迟队列:DLX + TTL 的“神仙组合” ✨📦⏳
- DLX + TTL 实现延迟队列的代码配置(简要回顾)
- RabbitMQ 官方 Delay Exchange 插件:更原生的延迟方案!
- 总结:死信、TTL 与延迟队列 📜
🌟我的其他文章也讲解的比较有趣😁,如果喜欢博主的讲解方式,可以多多支持一下,感谢🤗!
🌟了解 MQ 请看 : 【MQ篇】初识MQ!
其他优质专栏: 【🎇SpringBoot】【🎉多线程】【🎨Redis】【✨设计模式专栏(已完结)】…等
如果喜欢作者的讲解方式,可以点赞收藏加关注,你的支持就是我的动力
✨更多文章请看个人主页: 码熔burning
引言:消息不死,只是变成死信?
朋友们!👋 咱们之前聊了 RabbitMQ 消息的可靠传输,确保消息能从生产者安全到达队列,不丢不失。但是,消息进了队列,就万事大吉了吗?图样图森破!🙅♀️ 消息在队列里可能会遇到各种“意外”,导致它无法被正常消费。比如:
- 消费者处理不了,直接跟你“撂挑子”拒绝了!
- 消息在队列里待太久,过期了!
- 队列消息爆满了,新来的消息没地儿去,老消息就被挤掉了!
这些“命运多舛”的消息,RabbitMQ 给它们起了一个特别的名字——死信(Dead Letter)!👻✉️
那么问题来了,这些“死掉”的消息,RabbitMQ 会怎么处理呢?直接丢进回收站吗?当然不是(除非你没配置好)!对于重要的消息,每一条都不能轻易放弃!这时候,就要请出咱们今天的主角——死信交换机(Dead Letter Exchange - DLX) 登场了!它就像 RabbitMQ 里的“问题包裹回收中心”或者“死信中转站”,专门负责接收和处理这些来自“五湖四海”的死信!📦➡️💀➡️🔄
初识死信交换机:死信从哪来?DLX 干啥的?
什么是死信?
当一个队列中的消息满足下列情况之一时,可以成为死信(dead letter):
- 消费者使用
basic.reject
或basic.nack
声明消费失败,并且消息的requeue
参数设置为false
。这是最常见的主动制造死信的方式!“我不要这烫手山芋,你也别再发给我了,按死信处理吧!” 👋❌ - 消息是一个过期消息,超时无人消费。消息或者它所在的队列设置了存活时间(TTL),时间到了还没被消费,就“自然死亡”了。🕰️👻
- 要投递的队列消息满了,无法投递。队列像个仓库,容量有限,满了再来货,最老的可能被挤压“致死”。📏💀
我将用图片来展示了消息被消费者拒绝 (requeue=false
) 后变成死信的过程:
什么是死信交换机 (DLX)?
DLX 并不是 RabbitMQ 里一种全新的交换机类型。它就是一个普通的交换机(可以是 Direct、Topic 或 Fanout),只不过它被某个普通队列指定为了接收死信的“专属通道”!
如果一个包含死信的队列(比如 simple.queue
)配置了 dead-letter-exchange
属性,指定了一个交换机(比如 dl.direct
),那么队列中的死信就不会被丢弃,而是投递到这个指定的交换机中。这个被指定的交换机,就是死信交换机 (DLX)。
如果这个死信交换机也绑定了其他的队列,那么这些死信最终会进入这些队列,等待后续处理:
DLX 的核心作用: 就是作为一个“中转站”,收集各种原因产生的死信,并根据路由规则把它们发往最终的处理目的地(比如专门存放死信的队列、用于延迟重试的队列等)。
死信的旅程:如何从队列到达 DLX 并被路由?🗺️
队列将死信投递给死信交换机时,必须知道两个信息:死信交换机名称 和 死信交换机与死信队列绑定的 RoutingKey。这正是死信路由的关键所在!
-
指定 DLX 名称: 在普通队列声明时,通过
arguments
参数设置x-dead-letter-exchange
来指定死信要去的 DLX 名称。// QueueBuilder.durable("simple.queue") // 指定队列名称 // .deadLetterExchange("dl.direct") // ⭐ 这里指定死信交换机名称 ⭐ // .build();
这就是告诉 RabbitMQ,“我的死信都送去
dl.direct
!” -
死信的路由键: 当消息变成死信投递到 DLX 时,它需要一个路由键才能被 DLX 正确路由。这个路由键默认是原消息发送时的路由键。例如,原消息是发往
order.exchange
,路由键是"create"
,那么它变成死信后,默认会带着"create"
这个路由键发往 DLX。- 可选: 你也可以在普通队列声明时,通过
arguments
参数设置x-dead-letter-routing-key
来指定死信的路由键,这样所有从这个队列出来的死信都会使用这个指定的路由键,覆盖掉原路由键。
- 可选: 你也可以在普通队列声明时,通过
-
DLX 的绑定与路由: DLX 收到死信后,就像处理普通消息一样,根据自身的交换机类型和死信的路由键,查找匹配的绑定,将死信路由到与之绑定的队列(这就是“死信队列”,
dl.queue
就是一个例子)。(死信携带路由键到达 DLX,DLX 按绑定规则路由到死信队列)
完整的 Spring Boot 代码示例,演示了如何配置一个普通队列 (simple.queue
),让它的死信进入指定的 DLX (dl.direct
),然后这个 DLX 又绑定了一个专门接收死信的队列 (dl.queue
):
// producer服务CommonConfig中定义死信交换机、死信队列的代码
package cn.itcast.mq.config; // 假设你的包名import org.springframework.amqp.core.Binding; //
import org.springframework.amqp.core.BindingBuilder; //
import org.springframework.amqp.core.DirectExchange; //
import org.springframework.amqp.core.Queue; //
import org.springframework.amqp.core.QueueBuilder; //
import org.springframework.context.annotation.Bean; //
import org.springframework.context.annotation.Configuration; //@Configuration //
public class CommonConfig { //// ⭐ 定义业务队列,并配置其死信发往 DLX ⭐@Beanpublic Queue simpleQueue(){System.out.println("🛠️ 定义业务队列,指定死信交换机"); // 添加日志return QueueBuilder.durable("simple.queue") // 指定队列名称,并持久化 ✅.deadLetterExchange("dl.direct") // ⭐ 配置死信交换机为 dl.direct ⭐.build();}// ⭐ 声明死信交换机 dl.direct ⭐// 这个交换机用来接收来自 simple.queue 的死信@Beanpublic DirectExchange dlExchange(){System.out.println("🛠️ 声明死信交换机 dl.direct"); // 添加日志return new DirectExchange("dl.direct", true, false); // 持久化 ✅}// ⭐ 声明存储死信的队列 dl.queue ⭐// 这个队列绑定到 dl.direct 接收死信@Beanpublic Queue dlQueue(){System.out.println("🛠️ 声明存储死信的队列 dl.queue"); // 添加日志return new Queue("dl.queue", true); // 持久化 ✅}// ⭐ 将死信队列 dl.queue 与 死信交换机 dl.direct 绑定 ⭐// 绑定键要和从 simple.queue 出来的死信路由键匹配 (simple.queue 没有指定 deadLetterRoutingKey, 默认使用原路由键)// 假设原消息发送到 simple.queue 时使用的路由键是 "dl"@Beanpublic Binding dlBinding(){System.out.println("🛠️ 将死信队列 dl.queue 与 死信交换机 dl.direct 绑定,路由键为 'dl'"); // 添加日志return BindingBuilder.bind(dlQueue()).to(dlExchange()).with("dl"); // ⭐ 绑定键 "dl" ⭐}
}
注意:如果你之前已经用 @Bean
或 @RabbitListener
声明过 simple.queue
队列且没有指定 deadLetterExchange
属性,现在又用同样的名字多加了这个属性来声明,启动时会因为队列属性冲突而报错。你需要先删除 RabbitMQ 中的旧队列再启动应用。
死信交换机的使用场景总结 📜
- 如果队列绑定了死信交换机,死信会投递到死信交换机。这是机制本身。
- 可以利用死信交换机收集所有消费者处理失败的消息(死信),交由人工处理,进一步提高消息队列的可靠性。这是最直接的用途,创建一个专门的 DLQ,把所有失败消息都导进去,方便运维人员查看和处理。
除了提到的收集失败消息用于人工处理,死信交换机还有一个更重要、更常见的应用场景,那就是实现延迟队列和延迟重试!📦⏳ 这就引出了后面详细介绍的 TTL。
TTL:让消息“过期”变死信 🕰️
TTL (Time To Live) 就像消息的“生命倒计时”。一个队列中的消息如果超时未被消费,就会变为死信。超时的方式有两种:
-
消息所在的队列设置了超时时间: 在队列声明时配置
x-message-ttl
属性。进入这个队列的消息,如果超过队列设定的 TTL 时间还没被消费,就会死掉。// RabbitConfig.java @Bean public Queue ttlQueue(){System.out.println("🛠️ 声明一个带 TTL 的队列"); // 添加日志return QueueBuilder.durable("ttl.queue") // 持久化.ttl(10000) // ⭐ 设置队列消息的 TTL,10 秒 (10000 毫秒) ⭐.deadLetterExchange("dl.ttl.direct") // ⭐ 这个队列的死信发往 dl.ttl.direct ⭐.build(); } // 这个队列的死信交换机是 dl.ttl.direct,你需要定义它并绑定接收死信的队列 // @Bean public DirectExchange dlTtlExchange() { ... } // @Bean public Queue dlTtlQueue() { ... } // @Bean public Binding dlTtlBinding(...) { ... }
-
消息本身设置了超时时间: 在发送消息时给消息设置
expiration
属性。消息进入队列后,如果它本身的过期时间先于队列的 TTL 到期,或者队列没有设置 TTL,消息也会死掉。发送时设置消息 TTL 的代码示例:// 发送消息时设置 TTL 的代码 @Test // 这是一个测试方法 public void testTTLMsg() {System.out.println("📨 正在发送一条带 TTL 的消息"); // 添加日志// 创建消息Message message = MessageBuilder.withBody("hello, ttl message".getBytes(StandardCharsets.UTF_8)).setExpiration("5000") // ⭐ 设置消息本身的 TTL,5 秒 (5000 毫秒) ⭐.build();// 消息ID,需要封装到CorrelationData中CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());// 发送消息到 ttl.direct 交换机,路由键 "ttl"rabbitTemplate.convertAndSend("ttl.direct", "ttl", message, correlationData);log.debug("发送消息成功"); }
注意: 当队列和消息都设置了 TTL 时,两者之间 TTL 值小的那个会生效,先到期的那个会让消息变成死信。
延迟队列:DLX + TTL 的“神仙组合” ✨📦⏳
利用 TTL 让消息过期变成死信,再利用死信交换机把死信路由到其他地方,就实现了消息发出后不是立即被消费,而是延迟一段时间后才被处理的效果!这种模式就称为延迟队列 (Delay Queue) 模式。
延迟队列的经典场景:延迟发送短信、用户下单 15 分钟未支付自动取消订单、预约会议 20 分钟后通知参会人员等。
DLX + TTL 实现延迟队列的原理:
- 生产者发送一个需要延迟的消息到业务交换机,消息路由到业务队列。
- 业务队列被配置了死信交换机
x-dead-letter-exchange
指向 DLX。 - 消息进入业务队列后,因为我们最终想要它延迟消费,所以它不能被立即消费。它需要变成死信!这里最常用的是利用 TTL。
- 给业务队列或消息设置 TTL。消息在业务队列里等待 TTL 时间。⏰
- TTL 到期,消息变成死信。
- 因为业务队列配置了 DLX,死信被发送到 DLX。💀➡️
- DLX 绑定了一个新的队列,这个队列是真正的延迟队列。这个延迟队列没有消费者监听!它的唯一作用就是 “中转”和“等待” 。
- DLX 把死信路由到这个延迟队列。
- 消息在这个延迟队列里等待。关键点来了! 这个延迟队列也要配置
x-message-ttl
,并且这个 TTL 值就是你想要的延迟时间!⏳ - 消息在延迟队列里等待 TTL 时间到期后,再次变成死信。
- 这个延迟队列也要配置
x-dead-letter-exchange
,并且,它把死信发回原来的业务交换机!🤯 - 业务交换机收到消息,再次把它路由回原来的业务队列。
- 消费者监听的是原来的业务队列,于是它就收到了这条“延迟”后回来的消息!🎉
通过这个流程,消息就像在业务队列和“中转+等待”队列之间绕了个圈,成功实现了延迟消费。
DLX + TTL 实现延迟队列的代码配置(简要回顾)
你需要定义:
- 业务交换机和业务队列: 业务队列配置
x-dead-letter-exchange
指向你的 DLX。 - 死信交换机 (DLX): 一个普通交换机。
- 延迟队列: 配置
x-message-ttl
(延迟时间) 和x-dead-letter-exchange
指向业务交换机。 - 绑定: 将延迟队列绑定到 DLX,绑定键匹配业务队列死信的路由键。
这样,发送到业务队列的消息,如果设置了小于业务队列 TTL 的 TTL(或者业务队列没有 TTL),就会在业务队列里变成死信 -> 进入 DLX -> 进入延迟队列 -> 在延迟队列里等待 TTL -> 变成死信 -> 发回业务交换机 -> 回到业务队列被消费。
RabbitMQ 官方 Delay Exchange 插件:更原生的延迟方案!
RabbitMQ 官方提供的 Delay Exchange 插件!👏 如果你的 RabbitMQ 版本支持,使用这个插件实现延迟功能会更简单粗暴,不需要 DLX + TTL 这种“曲线救国”的方式。
-
原理: 声明一个类型为
x-delayed-message
并设置delayed = true
的交换机。当你发送消息到这个交换机时,消息会先被插件接收并持久化,然后读取消息头的x-delay
属性作为延迟时间。时间到期后,插件会模拟一次消息投递,把消息发送到该交换机绑定的队列。 -
使用方式:
- 安装 Delay Exchange 插件(自行查找安装教程)。
- 声明一个交换机,类型可以是任意类型 (如 Direct),但必须添加
delayed = true
属性。基于注解@Exchange(name = "delay.direct", delayed = "true")
或者基于 Bean 配置都可以。 - 发送消息时,在消息头里添加
x-delay
属性,值就是你想要的延迟时间(毫秒)。发送消息的代码示例:
// 发送延迟消息的示例 @Test // 测试方法 public void testDelayedMsg() {System.out.println("📨 正在发送一条使用 Delay Exchange 插件的延迟消息"); // 添加日志// 创建消息Message message = MessageBuilder.withBody("hello, delay message".getBytes(StandardCharsets.UTF_8)).setHeader("x-delay",10000) // ⭐ 在消息头设置 x-delay,指定延迟时间 10 秒 ⭐.build();// 消息ID,需要封装到CorrelationData中CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());// 发送消息到延迟交换机 delay.direct,路由键 "delay"rabbitTemplate.convertAndSend("delay.direct", "delay", message, correlationData);log.debug("发送消息成功"); }
使用 Delay Exchange 插件,流程更直观:消息 -> 延迟交换机 (等待) -> 延迟时间到 -> 发往绑定队列 -> 被消费者消费。省去了 DLX 和额外队列的周转。
总结:死信、TTL 与延迟队列 📜
我们可以更全面地理解死信、TTL 和延迟队列:
什么样的消息会成为死信?
- 消息被消费者
reject
或者nack
且requeue=false
。 - 消息超时未消费(消息或队列 TTL 到期)。
- 队列满了。
死信交换机的使用场景是什么?
- 收集所有消费者处理失败的消息(死信),交由人工处理,提高可靠性。
- 与 TTL 结合,实现消息的延迟队列功能。
消息超时的两种方式是?
- 给队列设置
x-message-ttl
属性。 - 给消息设置
expiration
属性。
如何实现发送一个消息 N 秒后消费者才收到消息? (使用 DLX + TTL 方案)
- 给消息的目标队列指定死信交换机 (
x-dead-letter-exchange
)。 - 声明一个延迟队列,设置
x-message-ttl
为 N 秒,并将其x-dead-letter-exchange
指向原业务交换机。 - 将这个延迟队列绑定到业务队列指定的死信交换机(绑定键匹配死信路由键)。
- 发送消息到业务队列,确保消息进入业务队列后不被立即消费(比如没有消费者或消费者收到后 NACK(false, false))。消息会在业务队列里因无人消费或被 NACK 变成死信,进入 DLX -> 进入延迟队列等待 N 秒 -> 过期变死信 -> 回到业务交换机 -> 回到业务队列被消费。
或者,更简单的方案是使用 RabbitMQ Delay Exchange 插件:
- 安装插件。
- 声明一个
delayed = true
的交换机。 - 将消费者监听的队列绑定到这个延迟交换机。
- 发送消息到这个延迟交换机,并在消息头设置
x-delay
为 N 毫秒。
死信交换机是 RabbitMQ 处理异常消息、实现延迟重试和构建复杂工作流的核心组件。理解了它的工作原理和配置方式,你就掌握了 RabbitMQ 消息高级玩法的敲门砖!🔑🚪
希望这篇超详细的死信交换机“攻略”能帮助你彻底吃透它!😊🚀
了解RabbitMQ消息不丢的“三板斧”请看:
【MQ篇】RabbitMQ的生产者消息确认实战!
【MQ篇】RabbitMQ之消息持久化!
【MQ篇】RabbitMQ的消费者确认机制实战!
了解RabbitMQ消息失败重试请看:
【MQ篇】RabbitMQ之消费失败重试!
相关文章:
【MQ篇】RabbitMQ之死信交换机!
目录 引言:消息不死,只是变成死信?初识死信交换机:死信从哪来?DLX 干啥的?什么是死信?什么是死信交换机 (DLX)?死信的旅程:如何从队列到达 DLX 并被路由?&…...
CI/CD解决方案TeamCity在游戏开发中的应用价值与优势分析
TeamCity是用于游戏开发的最流行的CI/CD工具之一。从独立开发者到3A工作室和游戏发行商,各种规模的公司都在使用。无论您在制作流程中使用何种工具,TeamCity都支持您为任何的工作流程设置全面的构建-测试-发布管道。 TeamCity如何增强您的游戏开发工作流…...
泰迪杯实战案例超深度解析:运输车辆安全驾驶行为分析与安全评价系统设计
(第七届泰迪杯数据挖掘挑战赛C题特等奖案例解析) 一、案例背景与目标 1.1 应用场景与痛点 在道路运输行业,不良驾驶行为(如急加速、急减速、疲劳驾驶)是引发交通事故的主要诱因,占事故总量的70%以上。某运输企业通过车联网系统采集了450辆运输车辆的高频数据(每秒1条)…...
C++初阶-模板初阶
目录 1.泛型编程 2.函数模板 2.1函数模板概念 2.2实现函数模板 2.3模板的原理 2.4函数模板的实例化 2.4.1隐式实例化 2.4.2显式初始化 2.5模板参数的匹配原则 3.类模板 3.1类模板定义格式 3.2类模板的实例化 4.总结 1.泛型编程 对广泛的类型法写代码,我…...
计算机网络自顶向下思维导图
主要就是记录下自己做的1-6章的思维导图,内容包含了每章每节内的重点内容 可能又错别字以及错误,欢迎指出 需要注意使用的是第七版的书 第一章 第二章 第二章二 第三章 第四章 第五章 第六章...
机器学习-入门-线性模型(1)
机器学习-入门-线性模型(1) 文章目录 机器学习-入门-线性模型(1)3.1 线性回归3.2 最小二乘解3.3 多元线性回归 3.1 线性回归 f ( x i ) w x i b 使得 f ( x i ) ≃ y i f(x_i) wx_i b \quad \text{使得} \quad f(x_i) \simeq y_i f(xi)wxib使得f(xi)≃yi 离散属性…...
Spark-Streaming3
无状态转换操作与有状态转换操作 无状态转换操作: 无状态转换操作仅处理当前时间跨度内的数据。例如,设置的采集时间为三秒,则只处理这三秒内的数据。 有状态转换操作(UpdateStateByKey): 有状态转换操作可以跨批次处理数据。涉及…...
【Pandas】pandas DataFrame rfloordiv
Pandas2.2 DataFrame Binary operator functions 方法描述DataFrame.add(other)用于执行 DataFrame 与另一个对象(如 DataFrame、Series 或标量)的逐元素加法操作DataFrame.add(other[, axis, level, fill_value])用于执行 DataFrame 与另一个对象&…...
【Spark入门】Spark简介:分布式计算框架的演进与定位
目录 1 大数据计算框架的演进历程 1.1 Hadoop MapReduce:第一代分布式计算框架 1.2 Spark的诞生与革新 2 Spark的核心架构与优势 2.1 Spark架构概览 2.2 Spark的核心优势解析 3 Spark的适用场景与定位 3.1 典型应用场景 3.2 技术定位分析 4 Spark与Hadoop…...
基于ArcGIS的洪水淹没分析技术-洪水灾害普查、风险评估及淹没制图中的实践技术
洪水灾害是全球面临的主要自然灾害之一,对人类社会和自然环境造成巨大影响。准确的洪水淹没分析对于灾害预防、风险评估及应急响应至关重要。ArcGIS作为一款强大的地理信息系统软件,在洪水淹没分析领域具有显著优势。ArcGIS的洪水淹没分析主要依赖于其强…...
【数据可视化-38】基于Plotly得泰坦尼克号数据集的多维度可视化分析
🧑 博主简介:曾任某智慧城市类企业算法总监,目前在美国市场的物流公司从事高级算法工程师一职,深耕人工智能领域,精通python数据挖掘、可视化、机器学习等,发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…...
数据库MySQL学习——day6(多表查询(JOIN)基础)
文章目录 1、关系型数据库中的表关系2、连接(JOIN)的基本概念3、INNER JOIN(内连接)3.1. 概念3.2. 语法结构 4、LEFT JOIN(左连接)4.1. 概念4.2. 语法结构 5、RIGHT JOIN(右连接)&am…...
Mysql如何高效的查询数据是否存在
文章目录 1. 三种方法2. 查询语句执行流程3. 三种方式对比 1. 三种方法 在业务中,我们有时候需要查询某个数据在数据表中是否存在,常见的方式有三种 SELECT COUNT(*) FORM tb_name WHERE conditionSELECT 1 FROM tb_name WHERE conditionSELECT EXISTS (SELECT 1 FROM tb_nam…...
MySQL快速入门篇---增删改查(下)
目录 一、修改(Update) 1.语法 2.示例 二、删除(Delete) 1.语法 2.示例 三、聚合函数 1.示例 1.1、COUNT 1.2、SUM 1.3、AVG 1.4、MAX 1.5、MIN 四、分组查询(GROUP BY) 1.语法 2.示例 …...
Mysql中隐式内连接和显式内连接的区别
1. 内连接(INNER JOIN) 内连接是数据库中一种常见的连接方式,用于从两个或多个表中返回满足连接条件的记录,即只返回两张表中匹配的行。 示例场景:有学生表(包含学生 ID 和姓名)和成绩表&…...
检测软件系统如何确保稳定运行并剖析本次检测报告?
检测软件系统,能及时找出问题,确保软件稳定运行,保障用户使用体验。下面会具体剖析本次检测报告。 检测概述 本次检测覆盖了系统性能、功能完整性等关键方面。它对软件整体状况做了严格评估。检测时对系统代码展开深度审查。还借助专业工具…...
【Office-Excel】单元格输入数据后自动填充单位
1.自定义设置单元格格式 例如我想输入数字10,回车确认后自动显示10kg。 右击单元格或者快捷键(Ctrl1),选择设置单元格格式,自定义格式输入: 0"kg"格式仍是数字,但是显示是10kg&…...
GAMES202-高质量实时渲染(Real-Time Shadows)
目录 Shadow MappingshadowMapping的问题shadow mapping背后的数学PCF(Percentage Closer Filtering)PCSS(Percentage closer soft shadows)VSSM(Variance Soft Shadow Mapping)优化步骤3优化步骤1SAT&…...
vue+neo4j+flask 音乐知识图谱推荐系统
文章结尾部分有CSDN官方提供的学长 联系方式名片 文章结尾部分有CSDN官方提供的学长 联系方式名片 关注B站,有好处! 编号: F027 架构: vueflaskneo4jmysql 亮点:协同过滤推荐算法知识图谱可视化 支持爬取音乐数据,数据超过3万条&…...
JavaScript 中 undefined 和 not defined 的区别
在 JavaScript 的调试过程中,你是否经常看到 undefined 却不知其来源?是否曾被 ReferenceError: xxx is not defined 的错误提示困扰?这两个看似相似的概念,实际上是 JavaScript 类型系统中最重要的分水岭。本文将带你拨开迷雾&am…...
设计一个新能源汽车控制系统开发框架,并提供一个符合ISO 26262标准的模块化设计方案。
今天,设计一个新能源汽车控制系统开发框架,并提供一个符合ISO 26262标准的模块化设计方案。以下为经过工业验证的技术方案: 一、系统架构设计 采用AUTOSAR Adaptive平台构建分布式系统,核心模块包括: 车辆控制单元(VC…...
基于STM32、HAL库的HX710A模数转换器ADC驱动程序设计
一、简介: HX710A是一款高精度24位模数转换器(ADC)芯片,专为电子秤和其他高精度测量应用设计。它通常与称重传感器(如应变片)配合使用,具有以下特点: 24位无失码精度 可编程增益:128或64 内置低噪声可编程放大器 片上稳压器,可直接为传感器供电 简单的数字接口(时钟+数据…...
python合并一个word段落中的run
在python-docx中,一个段落可以包含多个Run对象,每个Run对象可以具有不同的样式。如果你希望将一个段落中的所有Run对象合并为一个Run对象,同时保留所有文本内容,可以通过以下步骤实现: 合并Run对象的方法 遍历段落的…...
pytorch搭建并训练神经网络
#从小白开始学习人工智能# #学习笔记# 工具:pytorch 一、基础概念 1.神经网络是什么? 神经网络是人类受到生物神经细胞结构启发而研究出的算法体系。又称为人工神经网络(Artificial neural network) 最简版神经网络结构图&a…...
GCC 内建函数汇编展开详解
1. 引言 GNU 编译器集合(GCC)是广泛使用的开源编译器套件,支持多种编程语言,其中 C 语言编译器是其核心组件之一。在 C 语言编译过程中,GCC 不仅处理用户编写的标准 C 代码,还提供了一类特殊的函数——内建…...
iOS自定义电池电量显示控件 BatteryView 实现
iOS自定义电池视图:BatteryView 传送门:Android自定义电池电量显示控件 BatteryView 实现 在iOS开发中,自定义视图是提升用户体验的重要手段之一。本文将介绍如何通过Swift语言实现一个自定义的电池视图(BatteryView),并展示其功能和使用方法。 1. 功能概述 BatteryVi…...
LeetCode -- Flora -- edit 2025-04-27
1.接雨水 42. 接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 示例 1: 输入:height [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,…...
00-算法打卡-目录
1 数组 01-算法打卡-数组-二分查找-leetcode(704)-第一天-CSDN博客 02-算法打卡-数组-二分查找-leetcode(35)-第二天-CSDN博客 03-算法打卡-数组-二分查找-leetcode(34)-第三天_leetcode 34-CSDN博客 04-算法打卡-数组-二分查找-leetcode(69)-第四天-CSDN博客 05-算法打卡-数组…...
Vue2 与 Vue3 深度对比与技术解析
引言 Vue.js 是由尤雨溪创立的渐进式(progressive)JavaScript框架,自 2014 年发布以来,以其简单易用和灵活扩展性得到广泛应用。在 Vue2 的时代,它已经成为构建单页应用(SPA)和组件化开发的主流…...
Linux-UDP套接字编程
一.认识IP地址 IP 协议有两个版本, IPv4 和 IPv6. 我们之后凡是提到 IP 协议, 没有特殊说明的,默认都是指 IPv4。 IP 地址是在 IP 协议中, 用来标识网络中不同主机的地址;对于 IPv4 来说, IP 地址是一个 4 字节, 32 位的整数;我们通常也使用 "点分十进制" 的字符串表…...
tsconfig.json和tsconfig.node.json和tsconfig.app.json有什么区别
通过pnpm i vite 生成vue3项目时,会生成三个ts配置文件,分别是什么作用呢? 在Vue 3项目中,tsconfig.json、tsconfig.node.json和tsconfig.app.json是三个不同的TypeScript配置文件,它们分别用于不同的场景和目的。其中tsconfig.n…...
机器学习——Seaborn练习题
1、使用tips数据集,创建一个展示不同时间段(午餐/晚餐)账单总额分布的箱线图 import seaborn as sns import matplotlib.pyplot as plt import numpy as np import pandas as pdplt.rcParams["font.sans-serif"] ["SimHei"] plt.rcParams[&qu…...
Spring XML 外部实体(XXE)指南:示例和预防
什么是XXE? XML 文档遵循特定的标准。该标准强调了 XML 文档的构造方式,概述了有效 XML 文档与无效 XML 文档的区别等等。 该标准还指定了一个称为“实体”的术语。实体是某些内容的占位符。 实体可以是内部的,也可以是外部的(就像我们的情况一样)。 实体通过系统标识…...
C语言(3)—分支和循环
文章目录 一、程序的基本结构二、分支结构1. if语句2. if-else语句 三、关系与逻辑运算符1. 关系运算符2. 逻辑运算符 四、条件运算符(三目运算符)五、switch语句六、循环结构1. while循环2. for循环 七、循环控制语句1. break2. continue 八、循环嵌套九…...
【MCP】从一个天气查询服务带你了解MCP
1. 前言 这篇文章将通过一个集成高德天气查询的 MCP Server 用例,带你上手开发自己的 MCP Server ,文章将通过以下三种方式(自己编写 Client 端代码,使用 mcp-cli 自带页面,集成到 Claude 桌面版等)带你测试自己的 MC…...
【Leetcode 每日一题】3392. 统计符合条件长度为 3 的子数组数目
问题背景 给你一个整数数组 n u m s nums nums,请你返回长度为 3 3 3 的 子数组,满足第一个数和第三个数的和恰好为第二个数的一半。 子数组 指的是一个数组中连续 非空 的元素序列。 数据约束 3 ≤ n u m s . l e n g t h ≤ 100 3 \le nums.length…...
SALOME源码分析:Geomtry模块
本文分析SALOME Geometry模块。 一、核心组件 1.1 GeometryGUI 二、关键流程 三、插件 3.1 插件接口 GEOMPluginGUI定义了Geometry可以加载的插件接口。 3.2 插件列表 插件命令描述 BasicGUI BlocksGUI BooleanGUI BuildGUI DisplayGUI EntityGUI GenerationGUI GEOM…...
力扣HOT100之链表:23. 合并 K 个升序链表
这道题我是用最淳朴最简单的思路去做的,用一个while循环持续地将当前遍历到的最小值加入到合并链表中,while循环中使用一个for循环遍历整个指针数组,将其中的最小值和对应下标记录下来,并将其值加入到合并链表中,同时对…...
ArkTS 组件 通用事件 通用属性 速查表
ArkTS 组件 组件 通用事件 速查表 通用事件事件名称简要说明点击事件onClick(event: Callback<ClickEvent>, distanceThreshold: number): T相较于原有 onClick 接口,新增 distanceThreshold 参数作为点击事件移动阈值,当手指的移动距离超出所设…...
SOAP API 和 REST API
SOAP API 和 REST API 是两种主流的 Web 服务通信架构,它们在设计理念、数据格式、协议支持和应用场景上有显著差异。以下是两者的核心对比及典型应用场景: 1. 核心概念与设计哲学 特性SOAP APIREST API本质协议(基于 XML 的标准化协议&…...
简单了解Java的I/O流机制与文件读写操作
一、理解Java的I/O流机制 字节流 Java中的字节流主要由 InputStream 和 OutputStream 这两个抽象类及其子类构成。字节流以字节(byte)为基本处理单元,适用于处理所有类型的数据,包括文本、图片、音频、视频等。 1. InputStream…...
PCIe 转 U.2 接双硬盘指南 - 超微(Supermicro)主板
前言 公司服务器空间不够想扩容,尝试折腾了下超微(Supermicro)服务器的 PCIe 转 U.2,踩了一点小坑,特地写下来给大家参考一下。 现在市面上 U.2 接口的企业级固态硬盘相对其他类型接口的固态硬盘 便宜很多 ÿ…...
【上位机——MFC】文档
相关类 CDocument提供了一个用于管理数据的类,封装了关于数据的管理(数据提取、数据转换、数据存储等),并和视图类进行数据交互。 文档类使用 定义一个自己的文档类,派生自CDocument 程序的创建过程 1.利用框架类对象地址pFrame调用Load…...
JavaEE-多线程实战02
接上 多线程编程实战01 第三个多线程程序 package thread.test;//定义了一个叫MyThread3的类,实现了Runable接口,所以它必须重写run()方法 class MyThread3 implements Runnable {Overridepublic void run() {//线程执行的具体内容//进入一个无限循环,…...
计算机网络 | 应用层(6) -- 套接字编程
💓个人主页:mooridy 💓专栏地址:《计算机网络:自顶向下方法》 大纲式阅读笔记_mooridy的博客-CSDN博客 💓本博客内容为《计算机网络:自顶向下方法》第二章应用层第七节知识梳理 关注我…...
基于大模型的急性肠套叠全流程预测与诊疗方案研究报告
目录 一、引言 1.1 研究背景与目的 1.2 研究意义与创新点 二、急性肠套叠概述 2.1 定义与分类 2.2 病因与发病机制 2.3 流行病学特征 三、大模型技术原理与应用现状 3.1 大模型基本原理 3.2 在医疗领域的应用案例 3.3 用于急性肠套叠预测的可行性分析 四、术前风险…...
游戏遭遇DDoS攻击如何快速止损?实战防御策略与应急响应指南
是不是很抽象 我自己画的 一、游戏DDoS攻击特征深度解析 游戏行业DDoS攻击呈现复合型特征,2023年监测数据显示,针对游戏服务器的攻击中,63%采用UDP反射放大HTTP慢速攻击组合,攻击峰值达3.2Tbps。攻击者利用游戏协议特性ÿ…...
Linux电源管理(2)_常规的电源管理的基本概念和软件架构
原文: Linux电源管理(2)_Generic PM之基本概念和软件架构 1. 前言 Linux系统中那些常规的电源管理手段,包括关机(Power off)、待机(Standby or Hibernate)、重启(Reboot)等。这些…...
回文链表力扣234
思路: 对于这个题同样的找出题目的要求 1.判断回文 那么我们思考一下判断回文的方法,对于字符串我们只需要翻转一下就行,但是这不是通用的方法,在思考一下,我们是不是可以用双指针,一个在前一个在后,向中…...
互联网的下一代脉搏:深入理解 QUIC 协议
互联网的下一代脉搏:深入理解 QUIC 协议 互联网是现代社会的基石,而数据在其中高效、安全地传输是其运转的关键。长期以来,传输层的 TCP(传输控制协议)一直是互联网的主力军。然而,随着互联网应用场景的日…...