【MQ篇】RabbitMQ之消息持久化!

在这里插入图片描述

目录

    • 一、 交换机持久化 (Exchange Persistence)
    • 二、 队列持久化 (Queue Persistence)
    • 三、 消息持久化 (Message Persistence)
    • 四、 持久化的“黄金三角” 🔱:三者缺一不可!
    • 五、 来,完整的代码示例(整合持久化和确认机制)

🌟我的其他文章也讲解的比较有趣😁,如果喜欢博主的讲解方式,可以多多支持一下,感谢🤗!

🌟了解 MQ 请看 : 【MQ篇】初识MQ!

其他优质专栏: 【🎇SpringBoot】【🎉多线程】【🎨Redis】【✨设计模式专栏(已完结)】…等

如果喜欢作者的讲解方式,可以点赞收藏加关注,你的支持就是我的动力
✨更多文章请看个人主页: 码熔burning

咱们接着聊 RabbitMQ 的“靠谱”修炼之路!上一篇文章讲了生产者确认机制,现在聊聊它的“好记性”——持久化!💾✍️

你想想,咱们电脑里的数据,要么放在内存里(速度快,但关机就没),要么存在硬盘上(速度慢点,但断电还在)。RabbitMQ 也是一样!它处理消息得快,所以很多东西默认是放在内存里的。但如果 RabbitMQ 服务器突然“打个盹”😴 或者“哎呀,摔了一跤”💥(宕机或重启),内存里的东西就“唰”地一下都没了!😱 这可不行,重要的消息和设置怎么能说没就没呢?!

这时候,“持久化”就登场了!它的作用就是把 RabbitMQ 里那些重要的“记忆”写到硬盘上,这样即使服务器重启了,它也能从硬盘上把这些“记忆”读取回来,恢复到宕机前的状态。🐂

RabbitMQ 的持久化,主要涉及三个“记忆点”:

  1. 交换机持久化 (Exchange Persistence) - “分拣大厅的名字得刻石头上!” 🏛️
  2. 队列持久化 (Queue Persistence) - “每个收件人的信箱要钉牢!” 📬
  3. 消息持久化 (Message Persistence) - “信箱里的信要用不褪色的墨水写!” ✍️✉️

来,咱们一个个看,如何在代码里设置它们,以及为啥要这么做!

了解RabbitMQ生产者确认机制请看:【MQ篇】RabbitMQ的生产者消息确认实战!


一、 交换机持久化 (Exchange Persistence)

  • 啥是交换机? 它就像邮局的分拣大厅入口,负责接收信件(消息)并根据地址(路由键)扔到不同的传送带(队列)上。
  • 为啥要持久化? 如果交换机不持久化,RabbitMQ 重启后,这个交换机就“失忆”了,不存在了!😳 生产者再往这个名字的交换机发消息就会失败,因为“入口牌子”都没了。
  • 怎么设置? 在声明(创建)交换机的时候,把 durable 属性设为 true 就行了。
  • 代码怎么写? 在你的 RabbitConfig 里定义 Exchange 的 Bean 时:
// 交换机持久化设置
@Bean
public TopicExchange myDurableExchange() {// 参数1: 交换机名称// 参数2: durable 是否持久化,设为 true!✅// 参数3: autoDelete 是否自动删除,设为 false (通常不自动删除持久化交换机)System.out.println("🛠️ 正在创建持久化交换机: my.durable.exchange");return new TopicExchange("my.durable.exchange", true, false);
}

通过设置第二个参数为 true,你就告诉 RabbitMQ:“喂,这个叫 my.durable.exchange 的交换机很重要,给我记到小本本上!” ✍️

在这里插入图片描述

注意:默认情况下,由SpringAMQP声明的交换机都是持久化的。


二、 队列持久化 (Queue Persistence)

  • 啥是队列? 它就像收件人专属的信箱,消息最终会待在这里,等着被消费者取走。
  • 为啥要持久化? 如果队列不持久化,RabbitMQ 重启后,这个队列也“失忆”了,不见了!😵‍💫 不仅队列本身没了,更要命的是,如果这个队列里当时还存着没被消费的消息,它们也会跟着一起消失! 🗑️ 这绝对是消息丢失的重大风险点!
  • 怎么设置? 在声明(创建)队列的时候,把 durable 属性设为 true
  • 代码怎么写? 在你的 RabbitConfig 里定义 Queue 的 Bean 时:
// 队列持久化设置
@Bean
public Queue myDurableQueue() {// 参数1: 队列名称// 参数2: durable 是否持久化,设为 true!✅// 参数3: exclusive 是否独占 (通常不独占)// 参数4: autoDelete 是否自动删除System.out.println("🛠️ 正在创建持久化队列: my.durable.queue");return new Queue("my.durable.queue", true, false, false);
}

把第二个参数设为 true,你就给这个队列上了把“锁” 🔒,告诉 RabbitMQ:“这个信箱要钉死在这儿,重启了也得给我留着!”

在这里插入图片描述

注意:默认情况下,由SpringAMQP声明的队列都是持久化的。


三、 消息持久化 (Message Persistence)

  • 啥是消息? 就是你在队列里放着的那些“信件”本身的内容。
  • 为啥要持久化? 前面说了,队列持久化能保证队列这个“信箱”不消失。但是! 如果信箱里的“信件”本身没有做持久化处理(默认是放在内存里),那么即使信箱(队列)还在,里面的信件也会在 RabbitMQ 重启时“蒸发”!🌫️ 所以,为了让消息在持久化队列中真正“活下来”,消息本身也必须是持久化的!
  • 怎么设置? 在发送消息的时候,设置消息的 deliveryMode 属性为 persistent (模式 2)。
  • 代码怎么写? 在 Spring AMQP 中,你可以通过 MessagePostProcessor 来修改消息属性,或者通常情况下,如果你往一个持久化的队列发送消息,convertAndSend 方法在内部可能会帮你处理这个细节。但最明确的方式是手动设置消息属性:
// 消息持久化设置 (在发送消息时)
public void sendPersistentMessage(String message) {CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());System.out.println("📨 正在发送持久化消息: '" + message + "', ID: " + correlationData.getId());rabbitTemplate.convertAndSend("my.durable.exchange", "my.routing.key", message, new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {// ⭐ 设置消息的投递模式为持久化 (DeliveryMode.PERSISTENT) ⭐message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);System.out.println("✉️ 消息已标记为持久化!");return message;}}, correlationData); //别忘了带上 correlationData 给 ConfirmCallback 用!System.out.println("📬 持久化消息已提交到 RabbitTemplate,等待 RabbitMQ ACK...");
}

MessagePostProcessor 允许你在消息发送前“拦截”并修改消息的属性。这里我们就把它设置为 DeliveryMode.PERSISTENT。这就像在你写好的信封上盖个大红章 💌:“此件重要!请务必保存!”

注意:默认情况下,SpringAMQP发出的任何消息都是持久化的,不用特意指定。


四、 持久化的“黄金三角” 🔱:三者缺一不可!

理解持久化,最关键的是记住这个“黄金三角”:

只有当 交换机是持久化的 (durable exchange) + 队列是持久化的 (durable queue) + 消息是持久化的 (persistent message),消息才能在 RabbitMQ 重启后依然保存在队列中!

  • 如果队列不持久化,交换机和消息再怎么持久化也没用,队列没了,一切皆空。☁️
  • 如果队列持久化了,但消息不持久化,重启后队列还在,但里面的消息都没了。💧
  • 如果消息持久化了,但队列不持久化,同样队列没了消息也无法保存。
  • 交换机持久化主要是为了确保 Exchange 本身重启后还在,否则生产者都找不到它发消息了。

所以,为了确保消息在 RabbitMQ 宕机重启时不丢失,这三兄弟(持久化交换机、持久化队列、持久化消息)得协同工作才行!🤝


五、 来,完整的代码示例(整合持久化和确认机制)

咱们在之前的代码基础上,把持久化设置加进去,并且依然保留发送者确认机制,这俩功能是保护消息的左膀右臂!💪

1. pom.xml

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2. application.properties

# 配置Spring应用中的RabbitMQ连接参数
spring:rabbitmq:# RabbitMQ服务器的主机地址host: localhost# RabbitMQ服务器的端口号port: 5672# 访问RabbitMQ服务器的用户名username: guest# 访问RabbitMQ服务器的密码password: guest# 配置发布确认的类型为correlated,以便在消息发送后收到确认publisher-confirm-type: correlated# 启动返回机制,当消息无法投递时返回给发送者publisher-returns: true# 配置RabbitMQ模板的参数template:# 设置所有消息都是必须投递的mandatory: true# 设置等待回复的超时时间为60000毫秒reply-timeout: 60000# 配置日志级别
logging:level:# 设置org.springframework.amqp包下的日志级别为DEBUG,以便捕获AMQP相关的调试信息org:springframework:amqp: DEBUG

3. RabbitConfig.java

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;@Configuration
public class RabbitConfig {// 定义一个 ⭐持久化⭐ 主题交换机@Beanpublic TopicExchange myDurableExchange() {// 参数2: durable = true ✅System.out.println("🛠️ 正在创建持久化交换机: my.durable.exchange");return new TopicExchange("my.durable.exchange", true, false);}// 定义一个 ⭐持久化⭐ 队列@Beanpublic Queue myDurableQueue() {// 参数2: durable = true ✅System.out.println("🛠️ 正在创建持久化队列: my.durable.queue");return new Queue("my.durable.queue", true, false, false);}// 定义一个绑定,将持久化队列绑定到持久化交换机@Beanpublic Binding binding(Queue myDurableQueue, TopicExchange myDurableExchange) {// 使用固定的路由键 "my.routing.key"return BindingBuilder.bind(myDurableQueue).to(myDurableExchange).with("my.routing.key");}// 配置 RabbitTemplate 并设置 ConfirmCallback@Beanpublic RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);rabbitTemplate.setMandatory(true);// ⭐ 设置发送者确认回调 (用于确认消息是否到达 Exchange 并被接受路由) ⭐rabbitTemplate.setConfirmCallback(new ConfirmCallback() {@Overridepublic void confirm(@Nullable CorrelationData correlationData, boolean ack, @Nullable String cause) {String messageId = correlationData != null ? correlationData.getId() : "N/A";if (ack) {System.out.println("✨ RabbitMQ 确认收到消息并处理成功!Message ID: " + messageId + " (Ack: " + ack + ")");} else {System.err.println("💔 RabbitMQ 拒绝或未能处理消息!Message ID: " + messageId + ", 原因: " + cause + " (Ack: " + ack + ")");}}});// 这里省略了 ReturnCallback,它用于处理路由不到队列的消息 (需要 mandatory=true)// rabbitTemplate.setReturnsCallback(...)return rabbitTemplate;}// 简单的消费者(非持久化核心,用于演示消息能到达)// ⭐ 注意:这个消费者不会做消费者确认,消息消费后可能还在队列里,直到被 RabbitMQ 删除 ⭐// ⭐ 真正的持久化测试需要消费者确认并手动 ack ⭐@Beanpublic org.springframework.amqp.rabbit.listener.MessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory, Queue myDurableQueue) {SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();container.setConnectionFactory(connectionFactory);container.setQueues(myDurableQueue);container.setMessageListener(message -> {System.out.println("👂 消费者收到消息: '" + new String(message.getBody()) + "'");// 在实际应用中,这里处理完消息后需要手动发送 ACK 给 RabbitMQ// 如果不发 ACK 且设置了手动确认模式,消息会一直留在队列直到连接断开并重发// 这里为了简单演示持久化队列里有消息,设置为 NO_ACK 模式});// ⭐ 注意:AcknowledgeMode.NONE (不确认) 模式下,RabbitMQ 会立即删除消息,不利于演示重启后消息还在 ⭐// ⭐ 为了演示消息在队列中持久化,建议消费者代码暂时注释掉或者设置为手动确认且不手动 ack ⭐// ⭐ 本例代码中暂时保留消费者,但请注意其行为,真正的持久化测试需要手动停止消费者、重启RabbitMQ、再启动消费者查看 ⭐container.setAcknowledgeMode(AcknowledgeMode.NONE); // 简单演示,不处理消费者确认return container;}
}

4. MessageSender.java (发送持久化消息)

import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.UUID;@Component
public class MessageSender {@Autowiredprivate RabbitTemplate rabbitTemplate;// 使用持久化的交换机和队列名称private static final String EXCHANGE_NAME = "my.durable.exchange";private static final String QUEUE_NAME = "my.durable.queue"; // 虽然发送不需要队列名,这里记一下private static final String ROUTING_KEY = "my.routing.key";private static final String NON_EXISTENT_EXCHANGE = "non.existent.exchange";/*** 发送一条 ⭐持久化⭐ 消息到 ⭐持久化⭐ 交换机和队列* @param message 需要发送的消息内容*/public void sendPersistentMessage(String message) {CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());System.out.println("📨 正在发送持久化消息: '" + message + "', ID: " + correlationData.getId());rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING_KEY, message, new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {// ⭐ 核心:设置消息的投递模式为持久化 ⭐message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);System.out.println("✉️ 消息属性已设置为持久化!");return message;}}, correlationData);System.out.println("📬 持久化消息已提交到 RabbitTemplate,等待 RabbitMQ ACK...");}/*** 演示发送失败的情况,发送到一个不存在的 Exchange,预期会收到 NACK* 依然使用持久化属性,但因为 Exchange 不存在,消息无法到达 Exchange* @param message 需要发送的消息内容*/public void sendFailedPersistentMessage(String message) {CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());System.out.println("😠 尝试发送持久化消息到不存在的 Exchange: '" + NON_EXISTENT_EXCHANGE + "'");System.out.println("📨 正在发送失败消息: '" + message + "', ID: " + correlationData.getId());// 发送消息到不存在的交换机,并尝试标记为持久化(但消息根本到不了Exchange)rabbitTemplate.convertAndSend(NON_EXISTENT_EXCHANGE, ROUTING_KEY, message, new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {// 虽然设置了持久化,但这消息因为 Exchange 不存在,根本不会被 RabbitMQ 接收并存盘message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);return message;}}, correlationData);System.out.println("📬 失败消息已提交到 RabbitTemplate,等待 RabbitMQ NACK...");}
}

5. Application.java (主应用类)

import com.gewb.produce_confire.MessageSender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;import java.util.concurrent.TimeUnit;@SpringBootApplication
public class Application implements CommandLineRunner {@Autowiredprivate MessageSender messageSender;public static void main(String[] args) {SpringApplication.run(Application.class, args);// ⭐ 重要:为了让异步回调和持久化演示效果有时间展现,主线程不要马上退出 ⭐// 在非 Web 应用中,需要手动保持应用运行一段时间try {System.out.println("😴 应用正在运行,等待回调和可能的消费者消费。请手动停止 RabbitMQ 后再启动进行持久化测试。");TimeUnit.SECONDS.sleep(20); // 延长等待时间,以便观察日志并有时间手动操作 RabbitMQ} catch (InterruptedException e) {Thread.currentThread().interrupt();System.err.println("😴 应用被中断唤醒.");}System.out.println("👋 应用演示结束.");}@Overridepublic void run(String... args) throws Exception {System.out.println("🚀 应用启动,开始发送测试持久化消息...");// 1. 发送一条会成功投递到持久化队列的持久化消息messageSender.sendPersistentMessage("Important Persistent Message!");// 等待一下,让第一条消息的确认回调先执行TimeUnit.SECONDS.sleep(2);// 2. 发送一条会失败的消息(发送到不存在的 Exchange),看 NACK 回调messageSender.sendFailedPersistentMessage("This persistent message should be NACKED!");System.out.println("✅ 所有测试消息已发送提交。请观察日志输出中的 ACK/NACK 回调结果。");System.out.println(">>> 要测试持久化效果,请在看到 ACK 日志后,手动停止 RabbitMQ 服务器,然后重新启动,检查队列中是否有 'Important Persistent Message!' 这条消息。");}
}

运行结果:

在这里插入图片描述

注意,在 main 方法里,我加了更长的延时 (20 秒),并提示用户手动停止和启动 RabbitMQ 来测试持久化效果。

如何测试持久化效果?

  1. 确保 RabbitMQ 运行中。
  2. 运行上面的 Spring Boot 应用。
  3. 在控制台看到 ✨ RabbitMQ 确认收到消息并处理成功!Message ID: ... (ACK) 日志后,手动停止 RabbitMQ 服务器
  4. 检查 RabbitMQ 的数据目录(如果你知道在哪儿),可以看到有一些文件生成了(这些就是持久化数据)。
  5. 重新启动 RabbitMQ 服务器
  6. 使用 RabbitMQ Management Plugin (如果安装了) 或其他客户端工具,查看 my.durable.queue 这个队列。你会发现之前发送的 Important Persistent Message! 这条消息仍然在队列里!🥳 而发送到不存在 Exchange 的消息,因为根本没被 RabbitMQ 接受,当然不会出现在任何队列里。
  7. 如果你的 Spring Boot 应用还没退出(因为设置了 20 秒延时),RabbitMQ 重启后,它会自动尝试重连。如果连接成功,并且消费者容器也配置正确且连接成功,它可能会把队列里的消息消费掉(取决于你的消费者配置)。

通过这个实验,你就能亲眼看到“持久化”的神奇力量:它让你的重要消息熬过了服务器的重启!💪

小提示: 在实际生产环境中,为了确保消息不丢失,发送方确认 + 消费者确认 + 消息持久化 往往是同时使用的,它们从不同环节保障消息的安全。

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

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

相关文章

[AI技术(二)]JSONRPC协议MCPRAGAgent

Agent概述(一) AI技术基础(一) JSON-RPC 2.0 协议详解 JSON-RPC 2.0 是一种基于 JSON 的轻量级远程过程调用(RPC)协议,旨在简化跨语言、跨平台的远程通信。以下从协议特性、核心结构、错误处理、批量请求等角度进行详细解析: 一、协议概述 1. 设计原则 • 简单性:…

LeetCode238_除自身以外数组的乘积

LeetCode238_除自身以外数组的乘积 标签&#xff1a;#数组 #前缀和Ⅰ. 题目Ⅱ. 示例0. 个人方法一&#xff1a;暴力循环嵌套0. 个人方法二&#xff1a;前缀和后缀分别求积 标签&#xff1a;#数组 #前缀和 Ⅰ. 题目 给你一个整数数组 nums&#xff0c;返回 数组 answer &#…

算法笔记.spfa算法(bellman-ford算法的改进)

题目&#xff1a;&#xff08;来源于AcWing&#xff09; 给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c; 边权可能为负数。 请你求出 1 号点到 n 号点的最短距离&#xff0c;如果无法从 1 号点走到 n 号点&#xff0c;则输出 impossible。 …

07 Python 字符串全解析

文章目录 一. 字符串的定义二. 字符串的基本用法1. 访问字符串中的字符2. 字符串切片3. 字符串拼接4. 字符串重复5.字符串比较6.字符串成员运算 三. 字符串的常用方法1. len() 函数2. upper() 和 lower() 方法3. strip() 方法4. replace() 方法5. split() 方法 四. 字符串的进阶…

Java集成Zxing和OpenCV实现二维码生成与识别工具类

Java集成Zxing和OpenCV实现二维码生成与识别工具类 本文将介绍如何使用Java集成Zxing和OpenCV库&#xff0c;实现二维码的生成和识别功能。识别方法支持多种输入形式&#xff0c;包括File对象、文件路径和Base64编码。 一、环境准备 添加Maven依赖 <dependencies><…

【专题刷题】二分查找(二)

&#x1f4dd;前言说明&#xff1a; 本专栏主要记录本人的基础算法学习以及LeetCode刷题记录&#xff0c;按专题划分每题主要记录&#xff1a;&#xff08;1&#xff09;本人解法 本人屎山代码&#xff1b;&#xff08;2&#xff09;优质解法 优质代码&#xff1b;&#xff…

Java—ThreadLocal底层实现原理

首先&#xff0c;ThreadLocal 本身并不提供存储数据的功能&#xff0c;当我们操作 ThreadLocal 的时候&#xff0c;实际上操作线程对象的一个名为 threadLocals 成员变量。这个成员变量的类型是 ThreadLocal 的一个内部类 ThreadLocalMap&#xff0c;它是真正用来存储数据的容器…

Elasticsearch(ES)中的脚本(Script)

文章目录 一. 脚本是什么&#xff1f;1. lang&#xff08;脚本语言&#xff09;2. source&#xff08;脚本代码&#xff09;3. params&#xff08;参数&#xff09;4. id&#xff08;存储脚本的标识符&#xff09;5. stored&#xff08;是否为存储脚本&#xff09;6. script 的…

客户联络中心能力与客户匹配方式

在数字化时代&#xff0c;客户联络中心作为企业与客户沟通的核心枢纽&#xff0c;其服务能力与客户需求的精准匹配至关重要。随着客户期望的不断提升&#xff0c;传统的“一刀切”服务模式已难以满足个性化需求&#xff0c;如何通过智能化的手段实现服务能力与客户的高效匹配&a…

深入理解网络原理:UDP协议详解

在计算机网络中&#xff0c;数据的传输是通过各种协议实现的&#xff0c;其中用户数据报协议&#xff08;UDP&#xff0c;User Datagram Protocol&#xff09;作为一种重要的传输层协议&#xff0c;广泛应用于实时通信、视频流、在线游戏等场景。本文将深入探讨UDP协议的特性、…

vscode切换Python环境

跑深度学习项目通常需要切换python环境&#xff0c;下面介绍如何在vscode切换python环境&#xff1a; 1.点击vscode界面左上角 2.在弹出框选择对应kernel

【MCP Node.js SDK 全栈进阶指南】中级篇(4):MCP错误处理与日志系统

前言 随着MCP应用的规模和复杂性增长,错误处理与日志系统的重要性也日益凸显。一个健壮的错误处理策略和高效的日志系统不仅可以帮助开发者快速定位和解决问题,还能提高应用的可靠性和可维护性。本文作为中级篇的第四篇,将深入探讨MCP TypeScript-SDK中的错误处理与日志系统…

【Qt】文件

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Qt 目录 一&#xff1a;&#x1f525; Qt 文件概述 二&#xff1a;&#x1f525; 输入输出设备类 三&#xff1a;&#x1f525; 文件读写类 四&#xff1a;&#x1f525; 文件和目录信息类 五&…

代码随想录算法训练营第五十八天 | 1.拓扑排序精讲 2.dijkstra(朴素版)精讲 卡码网117.网站构建 卡码网47.参加科学大会

1.拓扑排序精讲 题目链接&#xff1a;117. 软件构建 文章讲解&#xff1a;代码随想录 思路&#xff1a; 把有向无环图进行线性排序的算法都可以叫做拓扑排序。 实现拓扑排序的算法有两种&#xff1a;卡恩算法&#xff08;BFS&#xff09;和DFS&#xff0c;以下BFS的实现思…

Qt实现语言切换的完整方案

在Qt中实现语言动态切换需要以下几个关键步骤&#xff0c;我将提供一个完整的实现方案&#xff1a; 一、准备工作 在代码中使用tr()标记所有需要翻译的字符串 cpp button->setText(tr("Submit")); 创建翻译文件 在.pro文件中添加&#xff1a; qmake TRANSLATION…

面试中被问到mybatis与jdbc有什么区别怎么办

1. 核心区别 维度JDBCMyBatis抽象层级底层API&#xff0c;直接操作数据库高层持久层框架&#xff0c;封装JDBC细节代码量需要手动编写大量样板代码&#xff08;连接、异常处理等&#xff09;通过配置和映射减少冗余代码SQL管理SQL嵌入Java代码&#xff0c;维护困难SQL与Java代…

用于协同显著目标检测的小组协作学习 2021 GCoNet(总结)

摘要 一 介绍 问题一&#xff1a;以往的研究尝试利用相关图像之间的一致性&#xff0c;通过探索不同的共享线索[12, 13, 14]或语义连接[15, 16, 17]&#xff0c;来助力图像组内的共同显著目标检测&#xff08;CoSOD&#xff09;&#xff0c;什么意思&#xff1f; 一方面是探…

OpenCV 图形API(62)特征检测-----在图像中查找最显著的角点函数goodFeaturesToTrack()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 确定图像上的强角点。 该函数在图像或指定的图像区域内找到最显著的角点&#xff0c;如文献[240]中所述。 函数使用 cornerMinEigenVal 或 cor…

MySQL引擎分类与选择、SQL更新底层实现、分库分表、读写分离、主从复制 - 面试实战

MySQL引擎分类与选择、SQL更新底层实现、分库分表、读写分离、主从复制 - 面试实战 故事背景&#xff1a; 今天&#xff0c;我们模拟一场互联网大厂Java求职者的面试场景。面试官将针对MySQL的核心技术点进行提问&#xff0c;涵盖MySQL引擎分类与选择、SQL更新底层实现、分库…

如何确保微型导轨的质量稳定?

微型导轨在精密机械中扮演着至关重要的角色&#xff0c;它们不仅影响设备的性能&#xff0c;还决定了产品的寿命。那么&#xff0c;如何通过一些关键步骤来提高微型导轨的稳定性呢&#xff1f; 1、严格筛选供应商&#xff1a;选择具备高品质保证能力的供应商&#xff0c;确保原…