科普文:微服务之Spring Cloud Alibaba消息队列组件RocketMQ工作原理
概叙
本文探讨 RocketMQ 的事务消息原理,并从源码角度进行分析,以及事务消息适合什么场景,使用事务消息需要注意哪些事项。
同时详细介绍RocketMQ 事务消息的基本流程,并通过源码分析揭示了其内部实现原理,尽管事务消息增加了系统的复杂性,但在需要保证消息一致性的场景中,它仍然是一种非常有效的解决方案,比如资金转账、订单处理、分布式事务、库存管理等场景。
什么是事务消息
事务消息是为了保证分布式系统中消息的一致性而引入的一种消息类型。事务消息允许消息发送方在发送消息后,进行本地事务操作,并根据本地事务的执行结果来决定消息的最终状态(提交或回滚)。
RocketMQ 事务消息的优缺点
优点
-
保证消息一致性:通过事务消息,RocketMQ 能够保证分布式系统中消息的一致性,避免数据不一致问题。
-
高性能:RocketMQ 的事务消息性能较高,能够满足高并发场景的需求。
-
易用性:RocketMQ 提供了简单易用的 API,使得开发者能够方便地使用事务消息。
缺点
-
复杂性:事务消息的引入增加了系统的复杂性,开发者需要处理事务状态回查等问题。
-
时延:事务消息的处理涉及
half
消息、回查等操作,可能会增加消息的时延。
事务消息适用场景
资金转账
在金融系统中,资金转账需要确保资金的一致性和安全性。例如,从账户 A 转账到账户 B,必须确保 A 的金额减少和 B 的金额增加是一个原子操作。使用事务消息可以保证在转账过程中,如果任何一个步骤失败,整个操作都会回滚,确保数据一致性。
订单处理
在电子商务系统中,订单处理通常涉及多个步骤,例如创建订单、扣减库存、生成支付记录等。这些步骤需要保证一致性。使用事务消息可以确保如果某一步操作失败,整个订单处理过程可以回滚,避免数据不一致。
分布式事务
在微服务架构中,分布式事务是一个常见的挑战。多个微服务之间的操作需要协调一致,事务消息可以作为一种分布式事务解决方案,确保各个微服务之间的数据一致性。
库存管理
在库存管理系统中,库存的增减操作需要保证一致性。例如,用户下单后需要扣减库存,使用事务消息可以确保在扣减库存失败时,订单状态不会被错误更新。
事务消息注意事项
确保本地事务的幂等性
在分布式系统中,本地事务操作可能会被多次执行。例如,在事务状态回查时,Broker 可能会多次检查本地事务状态。因此,确保本地事务操作的幂等性非常重要。幂等性可以确保多次执行相同的操作不会产生副作用。
设置合理的超时时间
事务消息的处理涉及half
消息、提交或回滚请求以及事务状态回查。设置合理的超时时间可以避免长时间等待,影响系统性能。超时时间应根据实际业务需求和系统性能进行调整。
处理事务状态回查
事务状态回查是事务消息的重要机制。当 Broker 在规定时间内没有收到提交或回滚请求时,会主动发起事务状态回查。开发者需要实现 TransactionCheckListener 接口,并在 checkLocalTransactionState 方法中处理回查逻辑,确保能够正确返回事务状态。
监控和日志
监控和日志是确保事务消息系统稳定运行的重要手段。通过监控,可以及时发现系统中的异常情况,例如事务状态回查失败、消息发送失败等。日志记录可以帮助开发者排查问题,分析系统性能。
资源隔离
在使用事务消息时,确保事务消息与其他普通消息的资源隔离,以避免相互影响。例如,可以为事务消息单独配置 Topic 和队列,确保事务消息的处理不受其他消息影响。
事务消息的重试机制
在某些情况下,事务消息的提交或回滚请求可能会失败。开发者需要考虑实现重试机制,以确保最终能够正确提交或回滚事务消息。重试机制可以通过定时任务或消息队列实现。
性能影响
事务消息的处理涉及多次网络通信和状态检查,可能会对系统性能产生一定影响。在高并发场景中,需要评估事务消息对系统性能的影响,并进行相应的优化。例如,可以通过批量处理、异步处理等方式提高性能。
RocketMQ事务消息(Transactional Message)
RocketMQ事务消息(Transactional Message)是指应用本地事务和发送消息操作可以被定义到全局事务中,要么同时成功,要么同时失败。RocketMQ的事务消息提供类似 X/Open XA 的分布式事务功能,通过事务消息能达到分布式事务的最终一致。
RocketMQ 事务消息的基本流程
RocketMQ 的事务消息是指在消息发送方发送消息后,需要经过两阶段提交来确保消息的可靠性传递和处理。
- Producer 发送 half 消息;
- Broker 先把消息写入 topic 是 RMQ_SYS_TRANS_HALF_TOPIC 的队列,之后给 Producer 返回成功;
- Producer 执行本地事务,成功后给 Broker 发送 commit 命令(本地事务执行失败则发送 rollback);
- Broker 收到 commit 请求后把消息状态更改为成功并把消息推到真正的 topic;
- Consumer 拉取消息进行消费。
代码如下:
public class ProducerTransactionListenerImpl implements TransactionListener {@Overridepublic LocalTransactionState executeLocalTransaction(Message msg, Object arg) {/*** 这里执行本地事务,执行成功返回LocalTransactionState.COMMIT_MESSAGE,执行失败返回* LocalTransactionState.ROLLBACK_MESSAGE,如果返回LocalTransactionState.UNKNOW,* Broker会回来查询,所以需要记录事务执行状态*/return LocalTransactionState.COMMIT_MESSAGE;}@Overridepublic LocalTransactionState checkLocalTransaction(MessageExt msg) {/*** 这里查询事务执行状态,根据事务状态返回LocalTransactionState.COMMIT_MESSAGE或* LocalTransactionState.ROLLBACK_MESSAGE,如果没有查询到返回LocalTransactionState.UNKNOW,* Broker会再次查询,可以记录查询次数,超过次数后返回ROLLBACK_MESSAGE*/return LocalTransactionState.UNKNOW;}
}
维度 8:消息索引
我们知道,RocketMQ 核心的数据文件有 3 个:CommitLog、ConsumeQueue 和 Index。其中Index 文件就是一个索引文件,结构如下图:
查找消息时,首先根据消息 key 的 hashcode 计算出 Hash 槽的位置,然后读取 Hash 槽的值计算 Index 条目的位置,从Index 条目位置读取到消息在 CommitLog 文件中的 offset,从而查找到消息。
在 Producer 发送消息时,可以指定一个 key,代码如下:
Message sendMessage = new Message("topic1", "tag1", message.getBytes());
sendMessage.setKeys("weiyiid");
这样可以通过 RocketMQ 提供的命令或者管理控制台来查询消息是否发送成功。
RocketMQ 的事务消息流程大致可以分为以下几个阶段:
-
发送
half
消息:生产者首先发送一条 "half
消息" 到 RocketMQ Broker。half
消息是指消息已经发送到 Broker,但此时消息状态不确定,该消息对消费者不可见。 -
执行本地事务:生产者在发送
half
消息之后,立即执行本地事务操作。例如,更新数据库、调用外部服务等。 -
提交或回滚事务消息:本地事务操作完成后,生产者根据本地事务的执行结果,向 Broker 发送 "提交" 或 "回滚" 请求。如果本地事务执行成功,则发送 "提交" 请求,使得
half
消息变为可消费的正式消息;如果本地事务失败,则发送 "回滚" 请求,Broker 将删除该half
消息。 -
事务状态回查:如果在规定时间内 Broker 没有收到提交或回滚请求,Broker 会主动向消息发送方发起事务状态回查,以确认该消息的最终状态。
Apache RocketMQ在4.3.0版中已经支持分布式事务消息,采用了2PC(两阶段提交)+ 补偿机制(事务状态回查)的思想来实现了提交事务消息,同时增加一个补偿逻辑来处理二阶段超时或者失败的消息,如上图所示。
RocketMQ 事务消息的源码分析
下面给出一个完整事务消息发送示例:
public class TransactionProducer {public static void main(String[] args) throws Exception {// 创建事务消息生产者TransactionMQProducer producer = new TransactionMQProducer("TransactionProducerGroup");producer.setNamesrvAddr("localhost:9876");// 设置事务状态回查监听器producer.setTransactionCheckListener(new TransactionCheckListener() {@Overridepublic LocalTransactionState checkLocalTransactionState(MessageExt msg) {// 处理事务状态回查逻辑System.out.println("Checking transaction state for message: " + new String(msg.getBody()));return LocalTransactionState.COMMIT_MESSAGE;}});// 启动生产者producer.start();// 发送事务消息Message msg = new Message("TransactionTopic", "TagA", "Transaction Message".getBytes());SendResult sendResult = producer.sendMessageInTransaction(msg, new LocalTransactionExecuter() {@Overridepublic LocalTransactionState executeLocalTransactionBranch(Message msg, Object arg) {// 执行本地事务逻辑System.out.println("Executing local transaction for message: " + new String(msg.getBody()));// 假设本地事务执行成功,返回 COMMIT_MESSAGE// 如果本地事务失败,返回 ROLLBACK_MESSAGEreturn LocalTransactionState.COMMIT_MESSAGE;}}, null);System.out.println("Send result: " + sendResult);// 阻塞主线程,防止退出System.in.read();// 关闭生产者producer.shutdown();}
}
客户端的事务消息处理
发送half
消息
发送half
消息的核心代码在 TransactionMQProducer 类中,通过 sendMessageInTransaction 方法实现:
public TransactionSendResult sendMessageInTransaction(Message msg, LocalTransactionExecuter tranExecuter, Object arg) {// 1. 发送`half`消息SendResult sendResult = this.defaultMQProducerImpl.send(msg);// 2. 执行本地事务LocalTransactionState localTransactionState = tranExecuter.executeLocalTransactionBranch(msg, arg);// 3. 根据本地事务状态提交或回滚消息this.endTransaction(msg, localTransactionState);return new TransactionSendResult(sendResult, localTransactionState);
}
在 sendMessageInTransaction 方法中,首先调用 send 方法发送half
消息,然后执行本地事务,并根据本地事务的结果调用 endTransaction 方法提交或回滚消息。
执行本地事务
本地事务的执行由 LocalTransactionExecuter 接口的实现类来完成。在实际使用中,用户需要实现该接口,并在 executeLocalTransactionBranch 方法中定义具体的本地事务逻辑。
public interface LocalTransactionExecuter {LocalTransactionState executeLocalTransactionBranch(final Message msg, final Object arg);
}
提交或回滚事务消息
提交或回滚事务消息的实现也在 TransactionMQProducer 类中,通过 endTransaction 方法完成:
private void endTransaction(Message msg, LocalTransactionState localTransactionState) {// 构建事务结束请求EndTransactionRequestHeader requestHeader = new EndTransactionRequestHeader();requestHeader.setCommitOrRollback(localTransactionState == LocalTransactionState.COMMIT_MESSAGE ? 0 : 1);requestHeader.setTranStateTableOffset(msg.getQueueOffset());requestHeader.setCommitLogOffset(msg.getCommitLogOffset());// 发送事务结束请求到 Brokerthis.defaultMQProducerImpl.endTransaction(msg, requestHeader);
}
在 endTransaction 方法中,根据本地事务的执行结果构建事务结束请求,并调用 endTransaction 方法将请求发送到 Broker。
事务状态回查
事务状态回查是由 Broker 发起的。当 Broker 在规定时间内没有收到提交或回滚请求时,会主动向消息发送方发起事务状态回查。回查的实现主要在 TransactionCheckListener 接口中:
public interface TransactionCheckListener {LocalTransactionState checkLocalTransactionState(final MessageExt msg);
}
消息发送方需要实现 TransactionCheckListener 接口,并在 checkLocalTransactionState 方法中定义如何检查本地事务的状态。
Broker 端的事务消息处理
Broker 端的事务消息处理主要在 TransactionalMessageServiceImpl 类中实现。Broker 负责接收half
消息、提交或回滚请求,并在必要时发起事务状态回查。
接收half
消息
Broker 接收half
消息的逻辑在 TransactionalMessageServiceImpl 类的 prepareMessage 方法中:
public PutMessageResult prepareMessage(MessageExtBrokerInner msgInner) {// 存储`half`消息return this.store.putMessage(msgInner);
}
提交或回滚消息
Broker 处理提交或回滚请求的逻辑在 TransactionalMessageServiceImpl 类的 commitMessage 和 rollbackMessage 方法中:
public boolean commitMessage(MessageExt msgExt) {// 提交消息return this.store.commitTransaction(msgExt);
}public boolean rollbackMessage(MessageExt msgExt) {// 回滚消息return this.store.rollbackTransaction(msgExt);
}
事务状态回查
Broker 发起事务状态回查的逻辑在 TransactionalMessageServiceImpl 类的 check 方法中:
public void check(long transactionTimeout, int transactionCheckMax, String topic) {// 遍历`half`消息队列,发起事务状态回查List<MessageExt> halfMessages = this.store.getHalfMessages(topic);for (MessageExt msg : halfMessages) {// 发起回查请求this.brokerController.getBroker2Client().checkProducerTransactionState(msg);}
}
RocketMQ分布式事务原理
分布式事务应用场景
随着应用的拆分,从单体架构变成分布式架构,那么每个服务或者模块也会有自己的数据库。一个业务流程的完成需要经过多次的接口调用或者多条MQ消息的发送。
那基于上面的应用场景,应该如何设计发送消息的流程,才能让这两个操作要么都成功,要么都失败呢?其实,可以参照XA两阶段提交的思想,把发送消息分成两步,然后把操作本地数据库也包括在这个流程中。
那么,在介绍原理之前,先科普一下两个新的概念:
1、半消息(Half Message):也就是暂不能投递消费者的消息。发送方已经将消息成功发送到了 MQ
服务端,但是服务端未收到生产者对这条消息的二次确认,这个时候,这条消息会被标记为“暂不能投递”状态。
2、消息回查(Message Status Check):由于网络闪断、生产者应用重启等原因,导致某条事务消息的
二次确认丢失,MQ 服务端通过扫描发现某条消息长期处于“半消息”时,需要主动向消息生产者询问该消息的最终状态,要么是Commit,要么Rollback。
如图所示,一共分为七个步骤
第一步:生产者向 MQ 服务端发送消息。
第二步:MQ 服务端将消息持久化成功之后,向发送方 ACK 确认消息已经发送成功,此时消息为半消息。
第三步:发送方开始执行本地数据库事务逻辑。
第四步:发送方根据本地数据库事务执行结果向 MQ Server 提交二次确认,MQ Server 收到 Commit状态则将半消息标记为可投递,订阅方最终将收到该消息;MQ Server 收到 Rollback 状态则删除半消息,订阅方将不会接受该消息。
第五步:在断网或者是应用重启的特殊情况下,按步骤4提交的二次确认最终未到达 MQ Server,经过固定时间后 MQ Server 将对该消息发起消息回查。
第六步:发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
第七步:发送方根据检查得到的本地事务的最终状态再次提交二次确认,MQ Server 仍按照步骤4对半消息进行操作(Commit/Rollback)。
RocketMQ事务消息使用限制
使用事务消息,有一些限制条件:
- 事务消息不支持延时消息和批量消息;
- 事务性消息可能不止一次被检查或消费,所以消费者端需要做好消费幂等;
为了避免单个消息被检查太多次而导致半队列消息累积,我们默认将单个消息的检查次数限制为 15 次(即默认只会回查15次),
我们可以通过 Broker 配置文件的 transactionCheckMax参数来修改此限制。
如果已经检查某条消息超过 N 次的话( N = transactionCheckMax ), 则 Broker 将丢弃此消息,并在默认情况下同时打印错误日志。
用户可以通过重写AbstractTransactionCheckListener 类来修改这个行为;
事务消息将在 Broker 配置文件中的参数 transactionMsgTimeout 这样的特定时间长度之后被检查。
当发送事务消息时,用户还可以通过设置用户属性 CHECK_IMMUNITY_TIME_IN_SECONDS 来改变这个限制,该参数优先于 transactionMsgTimeout 参数;
提交给用户的目标主题消息可能会失败,目前这依日志的记录而定。
它的高可用性通过 RocketMQ 本身的高可用性机制来保证,如果希望确保事务消息不丢失、并且事务完整性得到保证,建议使用同步的双重写入机制。
- 事务消息的生产者 ID 不能与其他类型消息的生产者 ID 共享。与其他类型的消息不同,事务消息允许反向查询、MQ服务器能通过它们的生产者 ID 查询到消费者。
- 1.事务消息执行时间限制:RocketMQ 要求事务消息的本地事务执行器(TransactionListener)在规定的时间内完成并返回事务执行结果,否则可能会触发回查机制。
- 2.回查机制的限制:如果发送方应用程序长时间未返回事务执行结果,RocketMQ 服务端会触发回查机制,这可能会增加系统的负担和网络开销。
- 3.不支持跨集群事务消息:RocketMQ 不支持跨集群的事务消息,即发送方和消费方需要处于同一个 RocketMQ 集群中。
- 4.事务消息的可靠性和性能权衡:由于事务消息需要经过两阶段提交,相比普通消息可能存在一定的性能损耗。
- 5.需要依赖本地事务执行器:发送方应用程序需要自行实现和注册本地事务执行器,确保本地事务的正确执行和结果反馈。
总的来说,RocketMQ 事务消息在确保消息可靠传递的同时,也需要开发者按照一定的规范来设计和实现本地事务执行器,以及处理可能的回查请求,这些都是在使用 RocketMQ 事务消息时需要考虑和遵循的限制。
RocketMQ事务消息怎么实现
在 RocketMQ 中,实现事务消息可以通过使用事务生产者(Transaction Producer)来完成。下面是一个简单的示例代码,演示了如何在 RocketMQ 中实现事务消息:
import org.apache.rocketmq.client.producer.TransactionListener;
import org.apache.rocketmq.client.producer.TransactionMQProducer;
import org.apache.rocketmq.common.message.Message;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class TransactionProducer {public static void main(String[] args) throws Exception {TransactionMQProducer producer = new TransactionMQProducer("transaction_producer_group");producer.setNamesrvAddr("your_namesrv_address");// 定义事务监听器TransactionListener transactionListener = new TransactionListenerImpl();producer.setTransactionListener(transactionListener);// 定义线程池来处理事务消息的预备、提交和回查ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 100, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2000), new ThreadFactory() {@Overridepublic Thread newThread(Runnable r) {Thread thread = new Thread(r);thread.setName("client-transaction-msg-check-thread");return thread;}});producer.setExecutorService(executor);producer.start();// 发送事务消息Message message = new Message("YourTopic", "YourTag", "YourKeys", "YourMsg".getBytes());producer.sendMessageInTransaction(message, null);// 关闭生产者producer.shutdown();}
}class TransactionListenerImpl implements TransactionListener {@Overridepublic LocalTransactionState executeLocalTransaction(Message msg, Object arg) {// 在此处执行本地事务,根据执行结果返回不同的状态return LocalTransactionState.COMMIT_MESSAGE; // or ROLLBACK_MESSAGE or UNKNOW}@Overridepublic LocalTransactionState checkLocalTransaction(MessageExt msg) {// 在此处检查本地事务的状态,并返回相应的状态return LocalTransactionState.COMMIT_MESSAGE; // or ROLLBACK_MESSAGE or UNKNOW}
}
以上代码中,我们创建了一个事务生产者 TransactionMQProducer,并设置了事务监听器 TransactionListener。
在事务监听器的实现中,我们需要实现 executeLocalTransaction 方法来执行本地事务,以及 checkLocalTransaction 方法来检查本地事务的状态。
在 executeLocalTransaction 中,根据本地事务的执行结果返回不同的状态,而在 checkLocalTransaction 中,根据本地事务的状态返回相应的状态。
使用事务消息时,需要确保消息发送的可靠性,以及本地事务的正确执行和状态的正确返回。在实际场景中,还需要根据业务逻辑来合理处理事务消息的执行和状态回查。
相关文章:

科普文:微服务之Spring Cloud Alibaba消息队列组件RocketMQ工作原理
概叙 本文探讨 RocketMQ 的事务消息原理,并从源码角度进行分析,以及事务消息适合什么场景,使用事务消息需要注意哪些事项。 同时详细介绍RocketMQ 事务消息的基本流程,并通过源码分析揭示了其内部实现原理,尽管事务消…...

黑马头条vue2.0项目实战(五)——首页—频道编辑
目录 1. 使用页面弹出层 1.1 页面弹出层简单使用 1.2 创建频道编辑组件 1.3 页面布局 2. 展示我的频道 3. 展示推荐频道列表 3.1 获取所有频道 3.2 处理展示推荐频道 4. 添加频道 5. 编辑频道 5.1 处理编辑状态 5.2 切换频道 5.3 让激活频道高亮 5.4 删除频道 6.…...
Java:基础语法
基础语法 1. 基本结构类和方法 2. 变量和数据类型基本数据类型引用数据类型 3. 操作符算术操作符比较操作符逻辑操作符 4. 控制结构条件语句循环语句 5. 数组6. 方法7. 面向对象编程类和对象继承多态 8. 异常处理9. 常用类库 1. 基本结构 类和方法 Java程序的基本单位是类&am…...
安装bedtools详细步骤和详细介绍bedtools用法
安装bedtools详细步骤和详细介绍bedtools用法 一、安装bedtools详细步骤下载解压安装编译依赖编译设置环境变量激活环境变量执行命令查看版本二、详细介绍bedtools用法使用bedtools示例用法bedtools intersectbedtools bamtobedbedtools window一、安装bedtools详细步骤 下载 …...

21 - grace数据处理 - 补充 - 泄露误差改正 - Slepian局部谱分析法(一) - slepian分析法理论理解
21 - grace数据处理 - 泄露误差改正 - Slepian局部谱分析法 - slepian分析法理论理解 0 引言1 slepian谱分析法1.1 slepian谱分析法AI解释1.2 基于slepian谱分析法的GRACE数据处理应用2 slepian谱分析法关键过程实现2.1 求解正定特征方程2.2 计算slepian基函数2.3 计算Shannon数…...

WLAN国家码与信道顺从表
国家码和信道顺从表及信道功率限制 不同的国家和地区规定了在本国或本地区可以使用的信道、射频信号在信道中的最大发射功率。工作在不同信道的射频信号,信号强度可能会有差别。国家码和信道顺从表、各信道的功率限制值、信道编号和频率对照关系请参见国家码和信道…...

行为型设计模式1:状态/策略/命令
行为型设计模式:状态/策略/命令 (qq.com)...

【知识专栏丨python数分实战】天猫订单数据分析及可视化|taobao天猫订单接口
今天这篇文章将给大家介绍天猫订单数据分析及可视化案例。 import pandas as pdimport numpy as npfrom pyecharts.charts import Pie,Bar,Line,Map,Map3D,Funnelfrom pyecharts import options as optsimport matplotlib.pyplot as pltimport warningsimport seaborn as snsfr…...
[kimi笔记]为什么csc.exe不可以双击运行
csc.exe 是 C# 编译器的可执行文件,它是 .NET Framework 的一部分,用于编译 C# 源代码文件( .cs 文件)生成可执行文件( .exe 文件)或其他类型的程序集。 csc.exe 不能通过双击运行的原因有以下几点&…...

护眼大路灯哪个牌子好?2024学生护眼大路灯推荐
护眼大路灯哪个牌子好?护眼大路灯不仅能够提供日常的光线照明,还模拟了太阳光光线,使在室内用眼学习也能够有着自然光般的舒适感,但现在市场上有许多对产品质量把控不过关、光线效果欠佳、存有安全隐患的劣质护眼大路灯产品&#…...

Vue项目中手搓滑动校验模块-demo
实现代码 SliderCheck.vue <template><div class"drag" ref"dragDiv"><div class"drag_bg" ref"dragBg"></div><div class"drag_text" ref"dragText">{{ confirmWords }}</di…...
Socket如何实现客户端和服务器间的通信
Socket 是实现网络通信的一种机制,它允许在不同主机之间的进程通过网络进行数据交换。下面我将简要介绍如何使用 Socket 实现客户端和服务器间的通信。 客户端-服务器通信步骤: 服务器端: 创建服务器端 Socket: 服务器端通过创…...

基于Spring boot + Vue的校园论坛
作者的B站地址:程序员云翼的个人空间-程序员云翼个人主页-哔哩哔哩视频 csdn地址:程序员云翼-CSDN博客 1.项目技术栈: 前后端分离的项目 后端:Springboot MybatisPlus 前端:Vue ElementUI 数据库: …...

RabbitMQ高级特性 - 生产者消息确认机制
文章目录 生产者消息确认机制概述confirm 代码实现return 代码实现 生产者消息确认机制 概述 为了保证信息 从生产者 发送到 队列,因此引入了生产者的消息确认机制. RabbitMQ 提供了两种解决方案: 通过事务机制实现.通过发送确认机制(confi…...

webpack的loader机制
webpack的loader机制 loader本质上就是导出函数的JavaScript模块。导出的函数,可以用来实现内容的转换。 /* * param{string|Buffer} content 源文件的内容 * param{object} [map] SourceMap数据 * param{any} [meta] meta数据,可以是任何数据 * */ fu…...

(STM32笔记)十一、通过EXTI外部中断实现 按键控制LED
我用的是正点的STM32F103来进行学习,板子和教程是野火的指南者。 之后的这个系列笔记开头未标明的话,用的也是这个板子和教程。 十一、通过EXTI外部中断实现 按键控制LED 十一、通过EXTI外部中断实现 按键控制LED1、按键模块按键原理图按键程序思路 2、中…...

假如家里太大了,wifi连不上了怎么办
最近有个土豪朋友抱怨,他家里太大了,一个路由器的Wi-Fi信号根本无法覆盖他们家的每个房间,都没办法上网看奥运会比赛了。(还好我是穷人,就没有这种烦恼T_T)。 然后我问他为何不用一个路由器作主路由器&…...

elementPlus 设置el-input文本域固定高度和禁止下拉
elementPlus 设置el-input文本域固定高度和禁止下拉 话不多说直接上代码 // resize"none" 禁止下拉<el-inputv-model"textarea"style"width: 240px"type"textarea"resize"none"placeholder"请输入"/>// 设…...
(转)领导人必过的三道关
为什么企业领导人享受优厚的待遇,为什么董事会对企业领导人千挑万选?因为企业生命如此脆弱,据美国《财 富》杂志报道,世界500强企业平均寿命40年,世界1000强企业平均寿命30年,一般跨国公司平均寿命10年。而就是这脆弱…...
速盾:cdn可以定时刷新缓存吗?
CDN(Content Delivery Network)是一种通过在全球各地分布的服务器上缓存和传送网站内容的技术,以提高用户访问速度和降低服务器负载。CDN的缓存机制可以减少用户对源服务器的请求次数,从而提高网站的响应速度和性能。但是…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...

解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...