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

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

一、消费队列负载均衡概览
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()) {
        // 线程等待20s
        this.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) {
        // 遍历消费者的每个topic
        for (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 + 同属一个消费组 的所有消费者ID
            List<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(), // 当前消费者ID
                        mqAll,
                        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
    

在这里插入图片描述
四、参考资料

  1. https://blog.csdn.net/Weixiaohuai/article/details/123898841
  2. https://www.cnblogs.com/alisystemsoftware/p/16935521.html

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

阿里云绑定域名

在阿里云安全组与宝塔安全放开8081端口 server {listen 8081;server_name www.whxyyds.top;charset utf-8;location / {root /home/ruoyi/projects/ruoyi-ui;try_files $uri $uri/ /index.html;index index.html index.htm;}location /prod-api/ {proxy_set_header …

为生信写的Python简明教程 | 视频10

开源生信 Python教程 生信专用简明 Python 文字和视频教程 源码在&#xff1a;https://github.com/Tong-Chen/Bioinfo_course_python 目录 背景介绍 编程开篇为什么学习Python如何安装Python如何运行Python命令和脚本使用什么编辑器写Python脚本Python程序事例Python基本语法 数…

第N4周:使用Word2vec实现文本分类

目录 二、数据预处理1.加载数据2.构建词典3.生成数据批次和迭代器 二、模型构建1.搭建模型2.初始化模型3.定义训练与评估函数 三、训练模型1.拆分数据集并运行模型2.测试指定数据 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&…

Spring Boot 系列2 -- 配置文件

目录 1. 配置文件的作用 2. 配置文件的格式 3. properties 配置文件说明 3.1 properties 基本语法 3.2 读取配置文件 3.3 properties 缺点 4.yml 配置文件说明 4.1 yml 基本语法 4.2 yml 使用进阶 4.2.1 yml 配置不同数据类型及 null 4.2.2 yml 配置读取 4.2.3 注意…

DPWWN1靶场详解

DPWWN1靶场详解 首先还是nmap -sP 192.168.102.0/24扫描到ip地址&#xff0c;然后对这个ip进行一个单独的扫描&#xff0c;发现这个靶场有一个mysql数据库&#xff0c;猜测可能会用到sql注入&#xff0c;但是没用到。 ip登陆到网页发现并没有什么可利用的 唯一的切入点也就数…

Java 动态规划 Leetcode 62. 不同路径

代码展示&#xff1a; class Solution {public int uniquePaths(int m, int n) {//定义dp数组//二维数组多增加一行一列&#xff0c;方便对数组进行初始化int[][]dpnew int[m1][n1];//初始化dp[0][1]1;//填充数组for(int i1;i<m;i){for(int j1;j<n;j){dp[i][j]dp[i-1][j…

基于springboot+Redis的前后端分离项目(七)-【黑马点评】

&#x1f381;&#x1f381;资源文件分享 链接&#xff1a;https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA?pwdeh11 提取码&#xff1a;eh11 发布笔记&#xff0c;点赞&#xff0c;点赞排行 达人探店1、达人探店-发布探店笔记2、 达人探店-查看探店笔记3、 达人探店-点赞功…

《网络安全标准实践指南》(72页)

导读 摘要&#xff1a;为指导网络数据安全风险评估工作&#xff0c;发现数据安全隐患&#xff0c;防范数据安全风险&#xff0c;依据《中华人民共和国网络安全法》《中华人民共和国数据安全法》《中华人民共和国个人信息保护法》等法律法规&#xff0c;参照数据安全相关国家标…

STM32寄存器点亮LED灯

一&#xff1a; 如何寄存器点灯 1&#xff1a;看单片机的原理图 找到LED灯 这个灯是 PB5引脚 看原理图可以看出 让GPIOB5输出低电平 就能点亮那么我们得让打开控制GPIOB5的时钟让GPIOB5 输出模式让GPIOB5低电平 二&#xff1a;看中文参考手册配置寄存器 2.1&#xff1a;打开管…

【Windows】Redis集群部署

集群是如何进行工作的 Redis采用哈希槽来处理数据与节点之间的映射关系&#xff0c;一个集群共有16384 个哈希槽&#xff0c;每个key通过 CRC16算法计算出一个16bit的值&#xff0c;再对16384取模&#xff0c;得到对应的哈希槽&#xff0c;集群通过维护哈希槽与节点的关系来得…

redis与分布式

主从复制 概念 主从复制&#xff0c;是指将一台Redis服务器的数据&#xff0c;复制到其他的Redis服务器。前者称为主节点(Master)&#xff0c;后者称为从节点(Slave)&#xff0c;数据的复制是单向的&#xff0c;只能由主节点到从节点。Master以写为主&#xff0c;Slave 以读为…

MySQL----MHA高可用

文章目录 一、MHA理论1.1什么是 MHA1.2MHA 的组成1.3MHA 的特点 二、MHA的一主两从部署实验设计故障修复步骤&#xff1a; 一、MHA理论 1.1什么是 MHA MHA&#xff08;MasterHigh Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。 MHA 的出…

【Django】Django框架使用指南

Django使用指南 作者简介&#xff1a;嗨~博主目前是长安大学软件工程专硕在读&#x1f4d8;&#xff0c;喜欢钻研一些自己感兴趣的计算机技术&#xff0c;求关注&#x1f609;&#xff01; 框架简介&#xff1a;Django是一个基于Python语言的开源Web应用框架&#xff0c;采用 M…

基于STM32FFT(快速傅里叶变换)音频频谱显示功能实现

+ v hezkz17进数字音频系统研究开发交流答疑 一实验效果 二 设计过程 要用C语言实现STM32频谱显示功能,可以按照以下步骤进行操作: 1 确保已经安装好了适当的开发环境和工具链,例如Keil MDK或者GCC工具链。 2 创建一个新的STM32项目,并选择适合的MCU型号。 3 配置G…

【数据挖掘】时间序列教程【九】

第5章 状态空间模型和卡尔曼滤波 状态空间模型通常试图描述具有两个特征的现象 有一个底层系统具有时变的动态关系,因此系统在时间上的“状态”t 与系统在时间的状态t−1有关 .如果我们知道系统在时间上的状态t−1 ,那么我们就有了我们需要知道的一切,以便对当时的状态进行推…

Android Zygote 启动流程

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: Android系统包含netd、servicemanager、surfaceflinger、zygote、media、installd、bootanimation 等基本服务&#xff0c;具体作用请看下图。 Android…

更开放、更高性能、更具规模,闪马智能布局AGI时代

7月6日&#xff0c;2023世界人工智能大会&#xff08;WAIC 2023&#xff09;在上海盛大开幕。本届大会以“智联世界 生成未来”为主题&#xff0c;聚焦通用人工智能发展&#xff0c;共话产业新未来。 8日上午&#xff0c;由上海闪马智能科技有限公司&#xff08;下称“闪马智能…

el-form实现其中一个填写即可的校验

<el-formref"form":model"formData":rules"formRules"label-width"130px"><el-row :gutter"24"><el-col :span"12"><el-form-item label"司机姓名 :" prop"driverName"…

西电_矩阵论_学习笔记

文章目录 【 第一章 线性空间 】【 第二章 范数 】【 第三章 矩阵函数 】【 第四章 矩阵分解 】【 第五章 矩阵特征值估计 】【 第六章 广义逆 】【 考试重点内容总结 】 这是博主2023春季西电所学矩阵论的思维导图&#xff08;软件是幕布&#xff09;&#xff0c;供大家参考&a…

GaussDB OLTP云数据库配套工具DRS

目录 一、前言 二、DRS定义与使用场景 1、DRS定义 2、DRS场景示意图 三、DRS核心功能 1、实时迁移管理 2、实时同步管理 3、备份迁移管理 4、数据订阅管理 5、实时灾备管理 四、小结 一、前言 华为GaussDB云数据库提供了配套的生态工具数据复制服务DRS。 DRS围绕云…