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

RocketMQ5.0.0消息消费<二> _ 消息队列负载均衡机制

目录

一、消费队列负载均衡概览

二、消费队列负载均衡实现

1. 负载均衡UML

2. 启动RebalanceService线程

3. PUSH模式负载均衡

三、负载均衡策略

四、参考资料


一、消费队列负载均衡概览

        RocketMQ默认一个主题下有4个消费队列,集群模式下同一消费组内要求每个消费队列在同一时刻只能被一个消费者消费。那么集群模式下多个消费者是如何负载主题的多个消费队列呢?并且如果有新的消费者加入时,消费队列又会如何重新分布呢?

        RocketMQ消费端每20s周期执行一次消费队列重新负载,每次进行队列重新负载时会从Broker实时查询当前消费组内所有消费者,并且对消息队列、消费者列表进行排序,这样新加入的消费者就会在队列重新分布时分配到消费队列从而消费消息。如下所示,是消息拉取与消费队列负载均衡的交互图。

消息拉取与消费队列负载均衡的交互流程

二、消费队列负载均衡实现

1. 负载均衡UML

2. 启动RebalanceService线程

        参考《RocketMQ5.0.0消息消费<一> _ PUSH模式的消息拉取》
章节,消费者启动时,当前消费者添加到MQClientInstance#consumerTable属性中,并启动MQClientInstance实例。启动MQClientInstance实例时,会启动org.apache.rocketmq.client.impl.consumer.RebalanceService消费队列负载均衡服务线程。下图所示是该线程run()调用链。

        以下代码是MQClientInstance维护整个JVM的所有生产者和消费者的属性。

// 生产者容器
private final ConcurrentMap<String/* 生产组 */, MQProducerInner> producerTable = new ConcurrentHashMap<>();
// 消费者容器
private final ConcurrentMap<String/* 消费组 */, MQConsumerInner> consumerTable = new ConcurrentHashMap<>();

        org.apache.rocketmq.client.impl.consumer.RebalanceService#run()周期20s执行负载均衡任务。-Drocketmq.client.rebalance. waitlnterval参数修改执行周期,默认20s

@Override
public void run() {log.info(this.getServiceName() + " service started");while (!this.isStopped()) {// 线程等待20sthis.waitForRunning(waitInterval);// topic下消费队列的负载均衡this.mqClientFactory.doRebalance();}log.info(this.getServiceName() + " service end");
}

        org.apache.rocketmq.client.impl.factory.MQClientInstance#doRebalance方法遍历MQClientInstance实例中所有消费组下消费者。每一个消费者DefaultMQPushConsumerImpl拥有一个org.apache.rocketmq.client.impl.consumer.RebalanceImpl对象(实现负载均衡),给每个消费者找到一个消费队列(重新负载)

// 消费队列负载均衡
public void doRebalance() {for (Map.Entry<String/* 消费组 */, MQConsumerInner> entry : this.consumerTable.entrySet()) {// 获取消费者MQConsumerInner impl = entry.getValue();if (impl != null) {try {// 消费者负载均衡impl.doRebalance();} catch (Throwable e) {log.error("doRebalance exception", e);}}}
}

3. PUSH模式负载均衡

        org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl#doRebalance是PUSH模式的负载均衡的入口方法,其调用链如下。

        每个消费者DefaultMQPushConsumerImpl拥有一个RebalanceImpl对象,其中org.apache.rocketmq.client.impl.consumer.RebalanceImpl#doRebalance方法是对消费者的所有订阅主题进行负载均衡,即:消费者的所有订阅主题重新分配一个或多个消费队列来进行消费。其代码如下。注意事项:

  • Map<String/* topic */, SubscriptionData> subTable:获取当前消费者订阅的主题信息;
  • rebalanceByTopic():每个主题进行重新负载均衡
/*** 对消费者订阅的每个topic进行消费队列重新负载* step1:获取消费者订阅的主题信息,注意:消费者可以订阅多个主题* step2:遍历消费者的每个topic* step3:消费者订阅的topic进行消费队列重新负载*        {@link RebalanceImpl#rebalanceByTopic(String, boolean)}* @param isOrder 是否顺序消息* @return true所有topic重新负载成功*/
public boolean doRebalance(final boolean isOrder) {boolean balanced = true;// 获取消费者订阅的主题信息,注意:消费者可以订阅多个主题Map<String/* topic */, SubscriptionData> subTable = this.getSubscriptionInner();if (subTable != null) {// 遍历消费者的每个topicfor (final Map.Entry<String, SubscriptionData> entry : subTable.entrySet()) {final String topic = entry.getKey();try {if (!clientRebalance(topic) && tryQueryAssignment(topic)) {balanced = this.getRebalanceResultFromBroker(topic, isOrder);} else {// 消费者订阅的topic进行消费队列重新负载balanced = this.rebalanceByTopic(topic, isOrder);}} catch (Throwable e) {if (!topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) {log.warn("rebalance Exception", e);balanced = false;}}}}this.truncateMessageQueueNotMyTopic();return balanced;
}

        org.apache.rocketmq.client.impl.consumer.RebalanceImpl#rebalanceByTopic方法是对每个主题进行重新负载均衡的核心逻辑,如下代码所示。 这里介绍集群模式下负载均衡,注意事项:

  • MQClientInstance#findConsumerIdList():从Broker上获取所有订阅该topic且同属一个消费组的所有消费者ID。
  • 对消费队列、消费者ID集合排序:原因是同一个消费组内视图一致,确保同一个消费队列不会被多个消费者分配
  • AllocateMessageQueueStrategy#allocate:根据均衡策略,获取当前消费者的消息队列。
  • RebalanceImpl#updateProcessQueueTableInRebalance:重新负载后,消费者对应的分配后的消息队列是否变化: 新增、删除(其他消费者占用)
/*** 消费者订阅的topic进行消费队列重新负载* 集群模式下的步骤:* step1:从主题订阅信息缓存表(topicSubscribeInfoTable)中获取当前topic的消费队列* step2:从Broker上获取所有订阅该topic + 同属一个消费组 的所有消费者ID* step3:对消费队列、消费者ID排序,很重要,原因是:同一个消费组内视图一致,确保同一个消费队列不会被多个消费者分配* step4:根据均衡策略,获取当前消费者的消息队列*        {@link AllocateMessageQueueStrategy#allocate}* step5:消费者对应的分配消息队列是否变化: 新增、删除(其他消费者占用)*        {@link RebalanceImpl#updateProcessQueueTableInRebalance}* @param topic 主题* @param isOrder 是否是顺序消息* @return true重新分配消息队列成功*/
private boolean rebalanceByTopic(final String topic, final boolean isOrder) {boolean balanced = true;switch (messageModel) {case BROADCASTING: {Set<MessageQueue> mqSet = this.topicSubscribeInfoTable.get(topic);if (mqSet != null) {boolean changed = this.updateProcessQueueTableInRebalance(topic, mqSet, isOrder);if (changed) {this.messageQueueChanged(topic, mqSet, mqSet);log.info("messageQueueChanged {} {} {} {}", consumerGroup, topic, mqSet, mqSet);}balanced = mqSet.equals(getWorkingMessageQueue(topic));} else {this.messageQueueChanged(topic, Collections.<MessageQueue>emptySet(), Collections.<MessageQueue>emptySet());log.warn("doRebalance, {}, but the topic[{}] not exist.", consumerGroup, topic);}break;}case CLUSTERING: {// 从主题订阅信息缓存表中获取当前topic的消费队列Set<MessageQueue> mqSet = this.topicSubscribeInfoTable.get(topic);// 从Broker上获取所有订阅该topic + 同属一个消费组 的所有消费者IDList<String> cidAll = this.mQClientFactory.findConsumerIdList(topic, consumerGroup);if (null == mqSet) {if (!topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) {this.messageQueueChanged(topic, Collections.<MessageQueue>emptySet(), Collections.<MessageQueue>emptySet());log.warn("doRebalance, {}, but the topic[{}] not exist.", consumerGroup, topic);}}if (null == cidAll) {log.warn("doRebalance, {} {}, get consumer id list failed", consumerGroup, topic);}if (mqSet != null && cidAll != null) {List<MessageQueue> mqAll = new ArrayList<MessageQueue>();mqAll.addAll(mqSet);/*消费队列、消费者ID排序很重要:同一个消费组内视图一致,确保同一个消费队列不会被多个消费者分配*/// 消费队列排序Collections.sort(mqAll);// 消费者ID排序Collections.sort(cidAll);// 均衡策略AllocateMessageQueueStrategy strategy = this.allocateMessageQueueStrategy;List<MessageQueue> allocateResult = null;try {// 根据均衡策略,获取当前消费者的消息队列allocateResult = strategy.allocate(this.consumerGroup,this.mQClientFactory.getClientId(), // 当前消费者IDmqAll,cidAll);} catch (Throwable e) {log.error("allocate message queue exception. strategy name: {}, ex: {}", strategy.getName(), e);return false;}Set<MessageQueue> allocateResultSet = new HashSet<MessageQueue>();if (allocateResult != null) {allocateResultSet.addAll(allocateResult);}// 消费者对应的分配消息队列是否变化: 新增、删除(其他消费者占用)boolean changed = this.updateProcessQueueTableInRebalance(topic, allocateResultSet, isOrder);if (changed) {log.info("client rebalanced result changed. allocateMessageQueueStrategyName={}, group={}, topic={}, clientId={}, mqAllSize={}, cidAllSize={}, rebalanceResultSize={}, rebalanceResultSet={}",strategy.getName(), consumerGroup, topic, this.mQClientFactory.getClientId(), mqSet.size(), cidAll.size(),allocateResultSet.size(), allocateResultSet);this.messageQueueChanged(topic, mqSet, allocateResultSet);}balanced = allocateResultSet.equals(getWorkingMessageQueue(topic));}break;}default:break;}return balanced;
}

        org.apache.rocketmq.client.impl.consumer.RebalanceImpl#updateProcessQueueTableInRebalance重新分配后消费队列集合与上次负载的分配集合是否改变(新增或删除)来重新拉取消息。如下代码所示。

  • 删除(消费队列分配给其他消费者):暂停消费并移除,且持久化待移除消费队列的消费进度。
  • 新增(缓存表没有的消费队列):

                step1:删除内存中该消费队列的消费进度;

                step2:创建broker的消费队列;

                step3:从磁盘中获取该消费队列的消费进度(若进度<0时,则根据配置矫正消费进度),创建拉取消息请求。

  • 新增消费队列:重新创建拉取请求PullRequest加入到PullMessageService线程中,唤醒该线程拉取消息RebalanceImpl#dispatchPullRequest。
  • 若是顺序消息:是局部顺序消息,尝试向Broker请求锁定该消费队列,锁定失败延迟时则重新负载。
/*** 消费者对应的分配消息队列是否变化* step1:消费队列缓存表中不在本次均衡分配的消费队列时,则暂停消费并移除,且持久化待移除消费队列的消费进度;* step2:本次均衡分配的消费队列不在消费队列缓存表中,则新增:*         1):删除内存中该消费队列的消费进度;*         2):创建broker的消费队列;*         3):从磁盘中获取该消费队列的消费进度(若进度<0时,则根据配置矫正消费进度),创建拉取消息请求*              {@link RebalanceImpl#computePullFromWhere}* step3: 新增消费队列,则创建{@link PullRequest}加入到{@link PullMessageService},唤醒该线程拉取消息*              {@link RebalanceImpl#dispatchPullRequest}* step4:顺序消息时,则尝试向Broker请求锁定该消费队列,锁定失败延迟重新负载* @param topic 主题* @param mqSet 本次均衡分配的消费队列* @param isOrder 是否顺序* @return true变化;false未改变*/
private boolean updateProcessQueueTableInRebalance(final String topic, final Set<MessageQueue> mqSet,final boolean isOrder) {boolean changed = false;// drop process queues no longer belong me 当前消费队列不在分配队列中HashMap<MessageQueue, ProcessQueue> removeQueueMap = new HashMap<MessageQueue, ProcessQueue>(this.processQueueTable.size());// 遍历当前消费队列缓存表Iterator<Entry<MessageQueue, ProcessQueue>> it = this.processQueueTable.entrySet().iterator();while (it.hasNext()) {Entry<MessageQueue, ProcessQueue> next = it.next();MessageQueue mq = next.getKey();ProcessQueue pq = next.getValue();// 是该topic的消费队列if (mq.getTopic().equals(topic)) {// 当前消费队列不在现有的分配消息队列中,则暂停消费、废弃当前消费队列并移除(分配给其他消费者)if (!mqSet.contains(mq)) {pq.setDropped(true);removeQueueMap.put(mq, pq);} else if (pq.isPullExpired() && this.consumeType() == ConsumeType.CONSUME_PASSIVELY) {pq.setDropped(true);removeQueueMap.put(mq, pq);log.error("[BUG]doRebalance, {}, try remove unnecessary mq, {}, because pull is pause, so try to fixed it",consumerGroup, mq);}}}// remove message queues no longer belong me 移除不在分配的消费队列for (Entry<MessageQueue, ProcessQueue> entry : removeQueueMap.entrySet()) {MessageQueue mq = entry.getKey();ProcessQueue pq = entry.getValue();/*判断是否将{@link MessageQueue}、{@link ProcessQueue}缓存表中移除a. 持久化待移除的{@link MessageQueue}消费进度;b. 顺序消息时,需先解锁队列*/if (this.removeUnnecessaryMessageQueue(mq, pq)) {this.processQueueTable.remove(mq);changed = true;log.info("doRebalance, {}, remove unnecessary mq, {}", consumerGroup, mq);}}// add new message queue 遍历本次负载均衡分配的消费队列,缓存表中没有,则新增的消费队列boolean allMQLocked = true; // 消费队列是否有锁定(顺序消息使用)List<PullRequest> pullRequestList = new ArrayList<PullRequest>();for (MessageQueue mq : mqSet) {// 新增的消费队列if (!this.processQueueTable.containsKey(mq)) {// 若是顺序消息,则尝试向Broker请求锁定该消费队列,锁定失败延迟重新负载if (isOrder && !this.lock(mq)) {log.warn("doRebalance, {}, add a new mq failed, {}, because lock failed", consumerGroup, mq);allMQLocked = false;continue;}// 删除内存中该消费队列的消费进度this.removeDirtyOffset(mq);// 创建broker的消费队列ProcessQueue pq = createProcessQueue(topic);pq.setLocked(true);// 从磁盘中获取该消费队列的消费进度(若进度<0时,则根据配置矫正消费进度),创建拉取消息请求long nextOffset = this.computePullFromWhere(mq);if (nextOffset >= 0) {ProcessQueue pre = this.processQueueTable.putIfAbsent(mq, pq);if (pre != null) {log.info("doRebalance, {}, mq already exists, {}", consumerGroup, mq);} else {log.info("doRebalance, {}, add a new mq, {}", consumerGroup, mq);// 创建拉取消息请求PullRequest pullRequest = new PullRequest();pullRequest.setConsumerGroup(consumerGroup);pullRequest.setNextOffset(nextOffset);pullRequest.setMessageQueue(mq);pullRequest.setProcessQueue(pq);pullRequestList.add(pullRequest);changed = true;}} else {log.warn("doRebalance, {}, add new mq failed, {}", consumerGroup, mq);}}}// 锁定消费队列失败,延迟重新负载if (!allMQLocked) {mQClientFactory.rebalanceLater(500);}// 将拉取消息对象{@link PullRequest}加入到{@link PullMessageService},唤醒该线程拉取消息this.dispatchPullRequest(pullRequestList, 500);return changed;
}

        根据RebalanceImpl#updateProcessQueueTableInRebalance来判定消费者对应的分配到的消息队列是否变化(新增或删除)时,若是新增,则先删除内存消费进度,再从Broker端获取该消费队列的消费进度;若是删除,持久化消费进度同时删除旧的消费队列。 

a. 删除操作

        org.apache.rocketmq.client.impl.consumer.RebalanceImpl#removeUnnecessaryMessageQueue负载均衡时删除未分配的消费队列,其调用链如下。

b. 新增操作

        先删除该消费队列旧的内存消费进度,执行方法RebalanceImpl#removeDirtyOffset,其调用链如下。

        再从Broker磁盘获取该消费队列消费进度,执行RebalanceImpl#computePullFromWhere,其调用链如下。 

三、负载均衡策略

        org.apache.rocketmq.client.consumer.AllocateMessageQueueStrategy是消费队列负载均衡策略的接口,其有6个实现类,UML图如下。其中:

  • AllocateMessageQueueAveragely:平均分配算法(默认),如:8个消息消费队列q1、q2、q3、q4、q5、q6、q7、q8,有3个消费者c1、c2、c3,则分配如下:

                c1:q1、q2、q3

                c2:q4、q5、q6

                c3:q7、q8

  • AllocateMessageQueueAveragelyByCircle:平均轮询算法,如:8个消息消费队列q1、q2、q3、q4、q5、q6、q7、q8,有3个消费者c1、c2、c3,则分配如下:

                c1:q1、q4、q7

                c2:q2、q5、q8

                c3:q3、q6

四、参考资料

RocketMQ(十三) RocketMQ负载均衡_每天都要进步一点点的博客-CSDN博客

https://www.cnblogs.com/alisystemsoftware/p/16935521.html

消费者负载均衡 | RocketMQ

RocketMQ5.0.0消息消费<一> _ PUSH模式的消息拉取_爱我所爱0505的博客-CSDN博客

相关文章:

RocketMQ5.0.0消息消费<二> _ 消息队列负载均衡机制

目录 一、消费队列负载均衡概览 二、消费队列负载均衡实现 1. 负载均衡UML 2. 启动RebalanceService线程 3. PUSH模式负载均衡 三、负载均衡策略 四、参考资料 一、消费队列负载均衡概览 RocketMQ默认一个主题下有4个消费队列&#xff0c;集群模式下同一消费组内要求每个…...

【数据库】MySQL数据库约束(六大约束)

目录 1.数据库约束 1.1约束类型 1.2 非空约束&#xff08;NOT NULL &#xff09; 1.3 唯一约束&#xff08;UNIQUE&#xff09; 1.4默认值约束&#xff08;DEFAULT &#xff09; 1.5主键约束&#xff08;PRIMARY KEY&#xff09; 1.6外键约束&#xff08;FOREIGN KEY &…...

使用inotify监视文件后台运行收到 SIGTTIN 信号的原因及解决方案

一、起因 由于之前写了个程序要实时监控指定文件的变化状态&#xff0c;所以使用了“inotify”进行监视。但是却发现用了“inotify”之后进程无法手动后台运行了。 也就是 ./process.exe &&#xff0c;这种方法不行了。 原因是&#xff1a; 当使用inotify监视文件变化时&a…...

L3-021 神坛

在古老的迈瑞城&#xff0c;巍然屹立着 n 块神石。长老们商议&#xff0c;选取 3 块神石围成一个神坛。因为神坛的能量强度与它的面积成反比&#xff0c;因此神坛的面积越小越好。特殊地&#xff0c;如果有两块神石坐标相同&#xff0c;或者三块神石共线&#xff0c;神坛的面积…...

ArrayList和LinkedList区别

List<TreeNode> list new ArrayList<TreeNode>(); List<TreeNode> allTrees new LinkedList<TreeNode>(); 这两行代码都是用来创建一个存储多个 TreeNode 对象的列表&#xff0c;但是它们使用的底层实现不同。 ArrayList 是一种数组实现的动态数组&…...

977. 有序数组的平方 1. 两数之和 349. 两个数组的交集

给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 示例 1&#xff1a; 输入&#xff1a;nums [-4,-1,0,3,10] 输出&#xff1a;[0,1,9,16,100] 解释&#xff1a;平方后&#xff0c;数组变为 …...

Mysql问题:[Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause

1 问题描述 使用Navicat连接到MySQL(版本&#xff1a;8.0.18)&#xff0c;执行查询&#xff1a; select * from t_user WHERE user_name admin查询结果没有问题&#xff0c;但是报错&#xff1a; [Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY claus…...

Idea springboot springCloud热加载热调试常用的两种方式

场景描述 在项目开发的过程中&#xff0c;需要修改调试的时候偶每次都需要重启项目浪费时间&#xff0c;下面是我整理的两种常用的两种方式方式一 修改启动配置方式&#xff08;主要针对debug模式下&#xff09; 点击启动配置》edit configrations… configration下面修改Upd…...

银河麒麟V10SP1高级服务器版本离线RPM方式升级openssl openssh 自动化升级系统补丁实战实例全网唯一

银河麒麟高级服务器操作系统简介&#xff1a; 银河麒麟高级服务器操作系统V10是针对企业级关键业务&#xff0c;适应虚拟化、云计算、大数据、工业互联网时代对主机系统可靠性、安全性、性能、扩展性和实时性等需求&#xff0c;依据CMMI5级标准研制的提供内生本质安全、云原生支…...

2023-3-9-一篇简短的文章把C++左右值关系讲的透透彻彻

目录前言C左值和右值二、右值引用二、右值引用前言 对于C的左值和右值相信很多人都没有一个很透彻的了解,之前我也是不懂的时候查阅了好多文章,但是讲完我还是一头雾水,直到我遇到一篇宝藏文章,讲的左值右值的关系以及Move函数的用法是相当的清楚,文章链接在这,话不多说讲解一…...

Vue3这样子写页面更快更高效

在开发管理后台过程中,一定会遇到不少了增删改查页面,而这些页面的逻辑大多都是相同的,如获取列表数据,分页,筛选功能这些基本功能。而不同的是呈现出来的数据项。还有一些操作按钮。 对于刚开始只有 1,2 个页面的时候大多数开发者可能会直接将之前的页面代码再拷贝多…...

锐捷AP设置限速(胖模式)

基于整个AP限速命令 Ruijie(config)#wlan-qos ap-based { per-user-limit | total-user-limit } { down-streams | up-streams } average-data-rate average-data-rate burst-data-rate burst-data-rate per-user-limit 对AP上的每个用户进行限速 …...

聚势合力,电巢与SDIA协会“战略合作签约仪式”圆满落成

前言&#xff1a; 2023年03月02日下午&#xff0c;电巢科技与深圳市平板显示行业协会齐聚深圳南山电巢XR演播厅&#xff0c;共同举办了隆重的战略合作签约仪式。 双方就数字化建设、品牌赋能、人才培养、技术创新等企业服务深入合作上达成一致&#xff0c;合力为产业赋能&…...

Linux安装后基础配置--网络--ssh--基本软件

安装教程比较多就不写了。 网络配置 设置虚拟网络 修改网络配置文件 vi /etc/sysconfig/network-scripts/ifcfg-ens33将ONBOOT由no改为yes&#xff1a; 修改为静态网络 /etc/sysconfig/network-scripts/ifcfg-eth33 BOOTPROTOstatic IPADDR192.168.1.129 GATEWAY192.168…...

剑指 Offer 66. 构建乘积数组

剑指 Offer 66. 构建乘积数组 难度&#xff1a;middle\color{orange}{middle}middle 题目描述 给定一个数组 A[0,1,…,n−1]A[0,1,…,n-1]A[0,1,…,n−1]&#xff0c;请构建一个数组 B[0,1,…,n−1]B[0,1,…,n-1]B[0,1,…,n−1]&#xff0c;其中 B[i]B[i]B[i] 的值是数组 AAA…...

1.1 误差的来源

不难发现&#xff0c;考察用计算机解决科学计算问题时所经历的几个环节&#xff08;如图1-1所示&#xff09;&#xff0c;其中每一步都可能产生误差&#xff0c;首先,数学模型是通过对实际问题进行抽象与简化得到的&#xff0c;它与实际问题之间有误差&#xff0e;数学模型与实…...

python进程间通信

进程间通信表示进程之间的数据交换。 为了开发并行应用程序&#xff0c;需要在进程间交换数据。 下图显示了多个子过程之间同步的各种通信机制 - 各种通信机制 队列 队列可以用于多进程程序。 多处理模块的Queue类与Queue.Queue类相似。 因此&#xff0c;可以使用相同的API…...

麒麟Linux操作系统磁盘策略永久调整为deadline

1.前言在安装数据库&#xff0c;比如达梦数据库时&#xff0c;为获取磁盘最佳性能&#xff0c;一般要将数据磁盘设置为deadline。2. 修改磁盘调度算法2.1临时修改假设磁盘为sda,echo deadline > /sys/block/sda/queue/scheduler2.2通用机永久修改grubby --update-kernelALL …...

yum安装Docker(CentOS7.9)

目录 一、安装环境 编写yum源(根据系统版本) 二、安装docker-ce 默认安装docker-ce是最新版本 ps&#xff1a;安装不成功则需要安装container-selinux&#xff0c;下载网络yum源,再安装docker-ce即可 #查看dcoker-ce所产生的文件路径 三、启动docker 四、配置镜像加速器…...

c++错误 free(): double free detected

记一次bug调试。。。。 我定义了一个类&#xff0c;测试的时候&#xff0c;调用它的方法出现了free(): double free detected &#xff0c;但是调用其他方法是正常的。这个错误&#xff0c;字面意思就是检测到了双重释放。是指对于同一块内存&#xff0c;释放了两次。 我的类…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU&#xff1a;如何让RNN变得更聪明&#xff1f; 在深度学习的世界里&#xff0c;循环神经网络&#xff08;RNN&#xff09;以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而&#xff0c;传统RNN存在的一个严重问题——梯度消失&#…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

第7篇:中间件全链路监控与 SQL 性能分析实践

7.1 章节导读 在构建数据库中间件的过程中&#xff0c;可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中&#xff0c;必须做到&#xff1a; &#x1f50d; 追踪每一条 SQL 的生命周期&#xff08;从入口到数据库执行&#xff09;&#…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving

地址&#xff1a;LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂&#xff0c;正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...