[RocketMQ] Broker 消息重放服务源码解析 (十三)

news2024/10/7 20:25:48

构建消息文件ConsumeQueue和IndexFile。

  1. ConsumeQueue: 看作是CommitLog的消息偏移量索引文件, 存储了它所属Topic的消息在Commit Log中的偏移量。消费者拉取消息的时候, 可以从Consume Queue中快速的根据偏移量定位消息在Commit Log中的位置。
  2. IndexFile索引文件: 看作是CommitLog的消息时间范围索引文件。IndexFile(索引文件)提供了一种可以通过key或时间区间来查询消息的方法。

ConsumeQueue和IndexFile, 加快了客户端的消费速度或者是查询效率。

文章目录

      • 1.ReputMessageService消息重放服务
      • 2.doReput执行重放
        • 2.1 isCommitLogAvailable是否需要重放
        • 2.2 getData获取重放数据
          • 2.2.1 selectMappedBuffer截取一段内存
        • 2.3 checkMessageAndReturnSize检查消息并构建请求
        • 2.4 doDispatch分发请求

1.ReputMessageService消息重放服务

ReputMessageService服务将会在循环中异步的每隔1ms对于写入CommitLog的消息进行重放, 将消息构建成为DispatchRequest对象, 然后将DispatchRequest对象分发给各个CommitLogDispatcher处理, 这些CommitLogDispatcher通常会尝试构建ConsumeQueue索引、IndexFile索引以及SQL92布隆过滤器。

在这里插入图片描述

/**
 * ReputMessageService的方法
 */
@Override
public void run() {
    DefaultMessageStore.log.info(this.getServiceName() + " service started");
    /*
     * 运行时逻辑
     * 如果服务没有停止,则在死循环中执行重放的操作
     */
    while (!this.isStopped()) {
        try {
            //睡眠1ms
            Thread.sleep(1);
            //执行重放
            this.doReput();
        } catch (Exception e) {
            DefaultMessageStore.log.warn(this.getServiceName() + " service has exception. ", e);
        }
    }

    DefaultMessageStore.log.info(this.getServiceName() + " service end");
}

每隔1ms执行一次duReput方法, 执行重放方法。

2.doReput执行重放

所谓的重放就是完成诸如ConsumeQueue索引、IndexFile索引、布隆过滤器、唤醒长轮询线程和被hold住的请求等操作。

  1. 如果重放偏移量reputFromOffset小于commitlog的最小物理偏移量, 那么设置为commitlog的最小偏移量, 如果重放偏移量小于commitlog的最大偏移量, 那么循环重放。
  2. 调用getData方法。根据reputFromOffset的物理偏移量找到mappedFileQueue中对应的CommitLog文件的MappedFile, 然后从该MappedFile中截取一段自reputFromOffset偏移量开始的ByteBuffer, 这段内存存储着将要重放的消息。
  3. 开始循环读取这段ByteBuffer中的消息, 依次重放。
    1. 如果存在消息, 调用checkMessageAndReturnSize, 检查当前消息的属性并且构建一个DispatchRequest对象返回。
    2. 调用doDispatch方法分发重放请求。
      1. CommitLogDispatcherBuildConsumeQueue: 根据DispatchRequest写ConsumeQueue文件, 构建ConsumeQueue索引。
      2. CommitLogDispatcherBuildIndex: 根据DispatchRequest写indexFile文件, 构建indexFile索引。
      3. CommitLogDispatcherCalcBitMap: 根据DispatchRequest构建布隆过滤器, 加速SQL92过滤效率, 避免每次都解析sql。
    3. 如果broker角色不是SLAVE, 且支持长轮询, 并且消息送达的监听器不为null, 那么通过该监听器的arriving方法触发调用pullRequestHoldService的pullRequestHoldService方法, 唤醒挂起的拉取消息请求, 表示有新的消息落盘, 可以进行拉取了。
    4. 如果读取到MappedFile文件尾, 那么获取下一个文件的起始索引继续重放。
/**
 * DefaultMessageStore的方法
 * <p>
 * 执行重放
 */
private void doReput() {
    //如果重放偏移量reputFromOffset小于commitlog的最小物理偏移量,那么设置为commitlog的最小物理偏移量
    if (this.reputFromOffset < DefaultMessageStore.this.commitLog.getMinOffset()) {
        log.warn("The reputFromOffset={} is smaller than minPyOffset={}, this usually indicate that the dispatch behind too much and the commitlog has expired.",
                this.reputFromOffset, DefaultMessageStore.this.commitLog.getMinOffset());
        this.reputFromOffset = DefaultMessageStore.this.commitLog.getMinOffset();
    }
    /*
     *
     * 如果重放偏移量小于commitlog的最大物理偏移量,那么循环重放
     */
    for (boolean doNext = true; this.isCommitLogAvailable() && doNext; ) {
        //如果消息允许重复复制(默认为 false)并且reputFromOffset大于等于已确定的偏移量confirmOffset,那么结束循环
        if (DefaultMessageStore.this.getMessageStoreConfig().isDuplicationEnable()
                && this.reputFromOffset >= DefaultMessageStore.this.getConfirmOffset()) {
            break;
        }
        /*
         * 根据reputFromOffset的物理偏移量找到mappedFileQueue中对应的CommitLog文件的MappedFile
         * 然后从该MappedFile中截取一段自reputFromOffset偏移量开始的ByteBuffer,这段内存存储着将要重放的消息
         */
        SelectMappedBufferResult result = DefaultMessageStore.this.commitLog.getData(reputFromOffset);
        if (result != null) {
            try {
                //将截取的起始物理偏移量设置为重放偏起始移量
                this.reputFromOffset = result.getStartOffset();
                /*
                 * 开始读取这段ByteBuffer中的消息,依次进行重放
                 */
                for (int readSize = 0; readSize < result.getSize() && doNext; ) {
                    //检查消息的属性并且构建一个DispatchRequest对象返回
                    DispatchRequest dispatchRequest =
                            DefaultMessageStore.this.commitLog.checkMessageAndReturnSize(result.getByteBuffer(), false, false);
                    //消息大小,如果是基于Dledger技术的高可用DLedgerCommitLog则取bufferSize
                    int size = dispatchRequest.getBufferSize() == -1 ? dispatchRequest.getMsgSize() : dispatchRequest.getBufferSize();

                    if (dispatchRequest.isSuccess()) {
                        //如果大小大于0,表示有消息
                        if (size > 0) {
                            /*
                             * 分发请求
                             * 1.  CommitLogDispatcherBuildConsumeQueue:根据DispatchRequest写ConsumeQueue文件,构建ConsumeQueue索引。
                             * 2.  CommitLogDispatcherBuildIndex:根据DispatchRequest写IndexFile文件,构建IndexFile索引。
                             * 3.  CommitLogDispatcherCalcBitMap:根据DispatchRequest构建布隆过滤器,加速SQL92过滤效率,避免每次都解析sql。
                             */
                            DefaultMessageStore.this.doDispatch(dispatchRequest);
                            //如果broker角色不是SLAVE,并且支持长轮询,并且消息送达的监听器不为null
                            if (BrokerRole.SLAVE != DefaultMessageStore.this.getMessageStoreConfig().getBrokerRole()
                                    && DefaultMessageStore.this.brokerConfig.isLongPollingEnable()
                                    && DefaultMessageStore.this.messageArrivingListener != null) {
                                //通过该监听器的arriving方法触发调用pullRequestHoldService的pullRequestHoldService方法
                                //即唤醒挂起的拉取消息请求,表示有新的消息落盘,可以进行拉取了
                                //这里涉及到RocketMQ的consumer消费push模式的实现,后面会专门讲解consumer消费
                                DefaultMessageStore.this.messageArrivingListener.arriving(dispatchRequest.getTopic(),
                                        dispatchRequest.getQueueId(), dispatchRequest.getConsumeQueueOffset() + 1,
                                        dispatchRequest.getTagsCode(), dispatchRequest.getStoreTimestamp(),
                                        dispatchRequest.getBitMap(), dispatchRequest.getPropertiesMap());
                                notifyMessageArrive4MultiQueue(dispatchRequest);
                            }
                            //设置重放偏起始移量加上当前消息大小
                            this.reputFromOffset += size;
                            //设置读取的大小加上当前消息大小
                            readSize += size;
                            //如果是SLAVE角色,那么存储数据的统计信息更新
                            if (DefaultMessageStore.this.getMessageStoreConfig().getBrokerRole() == BrokerRole.SLAVE) {
                                DefaultMessageStore.this.storeStatsService
                                        .getSinglePutMessageTopicTimesTotal(dispatchRequest.getTopic()).add(1);
                                DefaultMessageStore.this.storeStatsService
                                        .getSinglePutMessageTopicSizeTotal(dispatchRequest.getTopic())
                                        .add(dispatchRequest.getMsgSize());
                            }
                        } else if (size == 0) {
                            //如果等于0,表示读取到MappedFile文件尾
                            //获取下一个文件的起始索引
                            this.reputFromOffset = DefaultMessageStore.this.commitLog.rollNextFile(this.reputFromOffset);
                            //设置readSize为0,将会结束循环
                            readSize = result.getSize();
                        }
                    } else if (!dispatchRequest.isSuccess()) {

                        if (size > 0) {
                            log.error("[BUG]read total count not equals msg total size. reputFromOffset={}", reputFromOffset);
                            this.reputFromOffset += size;
                        } else {
                            doNext = false;
                            // If user open the dledger pattern or the broker is master node,
                            // it will not ignore the exception and fix the reputFromOffset variable
                            if (DefaultMessageStore.this.getMessageStoreConfig().isEnableDLegerCommitLog() ||
                                    DefaultMessageStore.this.brokerConfig.getBrokerId() == MixAll.MASTER_ID) {
                                log.error("[BUG]dispatch message to consume queue error, COMMITLOG OFFSET: {}",
                                        this.reputFromOffset);
                                this.reputFromOffset += result.getSize() - readSize;
                            }
                        }
                    }
                }
            } finally {
                result.release();
            }
        } else {
            //如果重做完毕,则跳出循环
            doNext = false;
        }
    }
}

2.1 isCommitLogAvailable是否需要重放

用于判断CommitLog是否需要执行重放。

/**
 * ReputMessageService的方法
 * CommitLog是否需要执行重放
 */
private boolean isCommitLogAvailable() {
    //重放偏移量是否小于commitlog的最大物理偏移量
    return this.reputFromOffset < DefaultMessageStore.this.commitLog.getMaxOffset();
}

2.2 getData获取重放数据

根据reputFromOffset的物理偏移量找到mappedFileQueue中对应的CommitLog文件的MappedFile, 然后从该MappedFile中截取一段自reputFromOffset偏移量开始的ByteBuffer, 这段内存存储着将要重放的消息。

/**
 * CommitLog的方法
 *
 * 获取CommitLog的数据
 */
public SelectMappedBufferResult getData(final long offset) {
    return this.getData(offset, offset == 0);
}

public SelectMappedBufferResult getData(final long offset, final boolean returnFirstOnNotFound) {
    //获取CommitLog文件大小,默认1G
    int mappedFileSize = this.defaultMessageStore.getMessageStoreConfig().getMappedFileSizeCommitLog();
    //根据指定的offset从mappedFileQueue中对应的CommitLog文件的MappedFile
    MappedFile mappedFile = this.mappedFileQueue.findMappedFileByOffset(offset, returnFirstOnNotFound);
    if (mappedFile != null) {
        //通过指定物理偏移量,除以文件大小,得到指定的相对偏移量
        int pos = (int) (offset % mappedFileSize);
        //从指定相对偏移量开始截取一段ByteBuffer,这段内存存储着将要重放的消息。
        SelectMappedBufferResult result = mappedFile.selectMappedBuffer(pos);
        return result;
    }

    return null;
}

2.2.1 selectMappedBuffer截取一段内存

从该MappedFile中截取一段自reputFromOffset偏移量开始的ByteBuffer, 这段内存存储着将要重放的消息。这段ByteBuffer和原mappedByteBuffer共享同一块内存, 但是拥有自己的指针。

然后根据起始物理索引、截取的ByteBuffer、截取的ByteBuffer大小以及当前CommitLog对象构建一个SelectMappedBufferResult对象返回。

/**
 * MappedFile的方法
 * @param pos 相对偏移量
 */
public SelectMappedBufferResult selectMappedBuffer(int pos) {
    //获取写入位置,即最大偏移量
    int readPosition = getReadPosition();
    //如果指定相对偏移量小于最大偏移量并且大于等于0,那么截取内存
    if (pos < readPosition && pos >= 0) {
        if (this.hold()) {
            //从mappedByteBuffer截取一段内存
            ByteBuffer byteBuffer = this.mappedByteBuffer.slice();
            byteBuffer.position(pos);
            int size = readPosition - pos;
            ByteBuffer byteBufferNew = byteBuffer.slice();
            byteBufferNew.limit(size);
            //根据起始物理索引、新的ByteBuffer、ByteBuffer大小、当前CommitLog对象构建一个SelectMappedBufferResult对象返回
            return new SelectMappedBufferResult(this.fileFromOffset + pos, byteBufferNew, size, this);
        }
    }

    return null;
}

2.3 checkMessageAndReturnSize检查消息并构建请求

检查这段内存中的下一条消息, 读取消息的各种属性即可, 不需要读取消息body, 根据属性构建一个DispatchRequest对象。

/**
 * CommitLog的方法
 *
 * @param byteBuffer 一段内存
 * @param checkCRC   是否校验CRC
 * @param readBody   是否读取消息体
 */
public DispatchRequest checkMessageAndReturnSize(java.nio.ByteBuffer byteBuffer, final boolean checkCRC,
                                                 final boolean readBody) {
    try {
        // 1 TOTAL SIZE
        //消息条目总长度
        int totalSize = byteBuffer.getInt();

        // 2 MAGIC CODE
        //消息的magicCode属性,魔数,用来判断消息是正常消息还是空消息
        int magicCode = byteBuffer.getInt();
        switch (magicCode) {
            case MESSAGE_MAGIC_CODE:
                break;
            case BLANK_MAGIC_CODE:
                //读取到文件末尾
                return new DispatchRequest(0, true /* success */);
            default:
                log.warn("found a illegal magic code 0x" + Integer.toHexString(magicCode));
                return new DispatchRequest(-1, false /* success */);
        }

        byte[] bytesContent = new byte[totalSize];
        //消息体CRC校验码
        int bodyCRC = byteBuffer.getInt();
        //消息消费队列id
        int queueId = byteBuffer.getInt();
        //消息flag
        int flag = byteBuffer.getInt();
        //消息在消息消费队列的偏移量
        long queueOffset = byteBuffer.getLong();
        //消息在commitlog中的偏移量
        long physicOffset = byteBuffer.getLong();
        //消息系统flag,例如是否压缩、是否是事务消息
        int sysFlag = byteBuffer.getInt();
        //消息生产者调用消息发送API的时间戳
        long bornTimeStamp = byteBuffer.getLong();
        //消息发送者的IP和端口号
        ByteBuffer byteBuffer1;
        if ((sysFlag & MessageSysFlag.BORNHOST_V6_FLAG) == 0) {
            byteBuffer1 = byteBuffer.get(bytesContent, 0, 4 + 4);
        } else {
            byteBuffer1 = byteBuffer.get(bytesContent, 0, 16 + 4);
        }
        //消息存储时间
        long storeTimestamp = byteBuffer.getLong();
        //broker的IP和端口号
        ByteBuffer byteBuffer2;
        if ((sysFlag & MessageSysFlag.STOREHOSTADDRESS_V6_FLAG) == 0) {
            byteBuffer2 = byteBuffer.get(bytesContent, 0, 4 + 4);
        } else {
            byteBuffer2 = byteBuffer.get(bytesContent, 0, 16 + 4);
        }
        //消息重试次数
        int reconsumeTimes = byteBuffer.getInt();
        //事务消息物理偏移量
        long preparedTransactionOffset = byteBuffer.getLong();
        //消息体长度
        int bodyLen = byteBuffer.getInt();
        if (bodyLen > 0) {
            //读取消息体
            if (readBody) {
                byteBuffer.get(bytesContent, 0, bodyLen);

                if (checkCRC) {
                    int crc = UtilAll.crc32(bytesContent, 0, bodyLen);
                    if (crc != bodyCRC) {
                        log.warn("CRC check failed. bodyCRC={}, currentCRC={}", crc, bodyCRC);
                        return new DispatchRequest(-1, false/* success */);
                    }
                }
            } else {
                //不需要读取消息体,那么跳过这段内存
                byteBuffer.position(byteBuffer.position() + bodyLen);
            }
        }
        //Topic名称内容大小
        byte topicLen = byteBuffer.get();
        byteBuffer.get(bytesContent, 0, topicLen);
        //topic的值
        String topic = new String(bytesContent, 0, topicLen, MessageDecoder.CHARSET_UTF8);

        long tagsCode = 0;
        String keys = "";
        String uniqKey = null;
        //消息属性大小
        short propertiesLength = byteBuffer.getShort();
        Map<String, String> propertiesMap = null;
        if (propertiesLength > 0) {
            byteBuffer.get(bytesContent, 0, propertiesLength);
            //消息属性
            String properties = new String(bytesContent, 0, propertiesLength, MessageDecoder.CHARSET_UTF8);
            propertiesMap = MessageDecoder.string2messageProperties(properties);

            keys = propertiesMap.get(MessageConst.PROPERTY_KEYS);
            //客户端生成的uniqId,也被称为msgId,从逻辑上代表客户端生成的唯一一条消息
            uniqKey = propertiesMap.get(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX);
            //tag
            String tags = propertiesMap.get(MessageConst.PROPERTY_TAGS);
            //普通消息的tagsCode被设置为tag的hashCode
            if (tags != null && tags.length() > 0) {
                tagsCode = MessageExtBrokerInner.tagsString2tagsCode(MessageExt.parseTopicFilterType(sysFlag), tags);
            }

            /*
             * 延迟消息处理
             * 对于延迟消息,tagsCode被替换为延迟消息的发送时间,主要用于后续判断消息是否到期
             */
            {
                //消息属性中获取延迟级别DELAY字段,如果是延迟消息则生产者会在构建消息的时候设置进去
                String t = propertiesMap.get(MessageConst.PROPERTY_DELAY_TIME_LEVEL);
                //如果topic是SCHEDULE_TOPIC_XXXX,即延迟消息的topic
                if (TopicValidator.RMQ_SYS_SCHEDULE_TOPIC.equals(topic) && t != null) {
                    int delayLevel = Integer.parseInt(t);

                    if (delayLevel > this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel()) {
                        delayLevel = this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel();
                    }

                    if (delayLevel > 0) {
                        //tagsCode被替换为延迟消息的发送时间,即真正投递时间
                        tagsCode = this.defaultMessageStore.getScheduleMessageService().computeDeliverTimestamp(delayLevel,
                                storeTimestamp);
                    }
                }
            }
        }
        //读取的当前消息的大小
        int readLength = calMsgLength(sysFlag, bodyLen, topicLen, propertiesLength);
        //不相等则记录BUG
        if (totalSize != readLength) {
            doNothingForDeadCode(reconsumeTimes);
            doNothingForDeadCode(flag);
            doNothingForDeadCode(bornTimeStamp);
            doNothingForDeadCode(byteBuffer1);
            doNothingForDeadCode(byteBuffer2);
            log.error(
                    "[BUG]read total count not equals msg total size. totalSize={}, readTotalCount={}, bodyLen={}, topicLen={}, propertiesLength={}",
                    totalSize, readLength, bodyLen, topicLen, propertiesLength);
            return new DispatchRequest(totalSize, false/* success */);
        }
        //根据读取的消息属性内容,构建为一个DispatchRequest对象并返回
        return new DispatchRequest(
                topic,
                queueId,
                physicOffset,
                totalSize,
                tagsCode,
                storeTimestamp,
                queueOffset,
                keys,
                uniqKey,
                sysFlag,
                preparedTransactionOffset,
                propertiesMap
        );
    } catch (Exception e) {
    }
    //读取异常
    return new DispatchRequest(-1, false /* success */);
}

2.4 doDispatch分发请求

ReputMessageService服务的核心代码, 循环调用DefaultMessageStore内部的dispatcherList中的CommitLogDispatcher的dispatch方法, 处理这个请求。

  1. CommitLogDispatcherBuildConsumeQueue: 根据DispatchRequest写ConsumeQueue文件, 构建ConsumeQueue索引。
  2. CommitLogDispatcherBuildIndex: 根据DispatchRequest写indexFile文件, 构建indexFile索引。
  3. CommitLogDispatcherCalcBitMap: 根据DispatchRequest构建布隆过滤器, 加速SQL92过滤效率, 避免每次都解析sql。
/**
 * DefaultMessageStore的方法
 *
 * @param req 分发请求
 */
public void doDispatch(DispatchRequest req) {
    //循环调用CommitLogDispatcher#dispatch处理
    for (CommitLogDispatcher dispatcher : this.dispatcherList) {
        dispatcher.dispatch(req);
    }
}

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

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

相关文章

【bash:xxx:command not found问题,在英伟达nvidia的jetson-orin-nx上遇到的>>>解决方式之一】

【bash:xxx:command not found问题,在英伟达nvidia的jetson-orin-nx上遇到的>>>解决方式之一】 1、概述2、实验环境3、问题描述&#xff1a;bash:xxx:command not found问题4、我的努力第一种方式&#xff1a;加入指令方式第二种方式&#xff1a;使用echo $PATH命令查…

设计模式(七)-----桥接模式(Bridge Pattern)

目录 什么是桥接模式优点缺点应用场景 基本结构业务场景不使用模式的解决方案实现发送普通消息实现发送加急消息实现发送特急消息添加发送手机消息的处理方式 使用桥梁模式来解决问题 什么是桥接模式 将抽象部分与他的实现部分分离,这样抽象化与实现化解耦,使他们可以独立的变…

Es直方图聚合--date_histogram

文章目录 1、背景2、bucket_key如何计算3、前置知识4、日历和固定时间间隔 4.1 Calendar intervals 日历间隔4.2 Fixed intervals 固定间隔 5、数据准备 5.1 准备mapping5.2 准备数据 6、聚合案例 6.1 dsl6.2 java代码6.3 聚合结果 7、完整代码8、参考文档 1、背景 此处来简单学…

云计算UPS监控,怎么办?

在大型数据机房中&#xff0c;UPS系统扮演着关键的角色&#xff0c;为计算机和网络设备提供可靠的电力备份。由于数据机房的规模庞大且关键性强&#xff0c;监控UPS系统的可靠性和效率至关重要。 UPS监控可以提供实时的电池状态、负载信息、电网电压等监测数据&#xff0c;并能…

c++中assert

参考:https://blog.csdn.net/bitcarmanlee/article/details/124283683 1.什么是assert assert&#xff0c;中文翻译为断言&#xff0c;注意是一个宏定义&#xff0c;不是函数。 c中&#xff0c;要使用assert&#xff0c;可以将cassert头文件include进来&#xff0c;而cassert最…

路径规划算法:基于孔雀优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于孔雀优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于孔雀优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法孔雀…

【HCIA】09.STP

STP的选举之发波原理 设备启动之后&#xff0c;经过选举会分别显示出它们的等级&#xff08;最强&#xff0c;次强&#xff0c;最弱&#xff09;选出等级之后&#xff0c;两两设备开始互相发波&#xff0c;等级强的设备会将弱的设备的光顶回去此时两两设备之间的波就是单方向的…

家政服务小程序软件解决方案

家政服务小程序软件是近年来随着人们对家政服务需求的增长而逐渐兴起的一种数字化服务解决方案。通过小程序软件&#xff0c;用户可以轻松预约家政服务&#xff0c;包括保姆、月嫂、钟点工等&#xff0c;而且价格透明、服务规范&#xff0c;大大提高了用户对家政服务的满意度。…

神经网络结构可视化-netron

网址&#xff1a;https://netron.app/ 点选择模型&#xff0c;将oonx文件拉到netron界面&#xff0c;即可 输出; 如何将pytorch模型转换为onnx的格式&#xff1f; 在测试&#xff08;训练好的模型&#xff09;里输入代码 to_onnx(model, 3, 28, 28, output/params.onnx)其…

【Linux】十分钟理解动静态库

目录 一 前置概念二 静态库2.12.22.3放入指定路径2.4 第三方库的使用 四 动态库3.1 环境变量3.2 软链接方案3.3 配置文件方案 一 前置概念 我们在VS2022下安装开发环境实际上就是安装编译器软件、安装要开发的语言配套的库和头文件。我们使用编译器有语法的自动提醒功能&#…

python常用库之colorama (python命令行界面打印怎么加颜色)

文章目录 python常用库之colorama (python命令行界面打印怎么加颜色)背景colorama介绍colorama使用colorama打印红色闪烁打印颜色组合 python常用库之colorama (python命令行界面打印怎么加颜色) 背景 在Python开发项目过程中&#xff0c;为了方便调试代码&#xff0c;经常会…

Java中Map中10w条数据用什么循环性能最好呢?

加油&#xff0c;新时代打工人&#xff01; 1、java中List集合三种获取集合元素方式 2、Java中Map使用增强for循环和迭代器获取key和value 选择合适的循环方式&#xff0c;让性能最优&#xff01; public class Test2 {public static void main(String[] args) {//初始化 10w…

vue upload 上传下载

目录 上传 下载 对象/文件流 download处理返回 文件流 axios.post 封装axios 1.请求设置类型responseType: blob 2.若有请求拦截(直接返回即可) 3.download 4.请求下载 相关基础 blob MIME vue 实现文件上传、下载的方法 - 掘金 上传 submitAddFile(){var form…

基础算法-【离散化】

离散化的本质&#xff1a;是建立了一段数列到自然数之间的映射关系&#xff08;value -> index)&#xff0c;通过建立新索引&#xff0c;来缩小目标区间&#xff0c;使得可以进行一系列连续数组可以进行的操作比如二分&#xff0c;前缀和等… 相应的算法模板&#xff1a; v…

【Linux】—— 进程地址空间

序言&#xff1a; 在上篇中&#xff0c;我们讲解了关于进程优先级的概念。本期&#xff0c;我将给大家介绍的是关于进程地址空间的话题。 目录 &#xff08;一&#xff09;程序地址空间回顾 &#xff08;二&#xff09;代码演示 &#xff08;三&#xff09;进程地址空间的引…

【力扣算法08】之 5. 最长回文子串 python

文章目录 问题描述示例1示例2提示 思路分析代码分析完整代码详细分析 运行效果截图调用示例运行结果 完结 问题描述 给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同&#xff0c;则该字符串称为回文字符串。 示例1 输入&#xff1a;s…

【网络编程】Linux服务器程序规范相关内容

文章目录 1、日志1.1、syslog()函数 2、用户信息2.1、UID、EUID、GID、EGID 3、进程间关系3.1、进程组3.2、会话 4、服务器程序后台化&#xff08;守护进程&#xff09; 1、日志 Linux提供一个守护进程rsyslogd来处理系统日志&#xff0c;系统日志中包括用户进程产生的日志以及…

VUE2基础-Vue实例

Vue 实例 创建一个 Vue 实例 每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的&#xff1a; var vm new Vue({// 选项 }) 虽然没有完全遵循 MVVM 模型&#xff0c;但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变量名…

交换:交换机相关技术(二层技术)

目录 VLAN&#xff1a;虚拟局域网 VLAN种类&#xff1a; 接口分配链路类型 接口划分VLAN 跨网段的通讯 VLAN&#xff1a;虚拟局域网 LAN &#xff1a;局域网 MAN&#xff1a;城域网 WAN&#xff1a;广域网 1.一个VLAN相当于一个广播域 VLAN&#xff1a;通过路由器和交换机…

PADS Logic如何对原理图页面进行操作呢?

在绘制复杂的原理图时&#xff0c;会根据功能模块来将原理图进行分页处理&#xff0c;在绘制原理图过程中&#xff0c;会对原理图页面进行一些处理&#xff0c;操作方法如下所列&#xff1a; 1、页面拷贝与粘贴&#xff1a;在原理图中选中需要复制的元件走线等&#xff0c;左击…