Seata服务端启动流程

news2024/10/5 14:06:31

1.run方法启动

当ServerApplication的run方法启动的时候会调用到run方法的callRunners方法中对实现了CommandLineRunner接口的类进行run方法的加载 而在seata中ServerRunner实现了CommandLineRunner接口所以会加载到ServerRunner 的run方法中实现seata服务端的启动

    @Override
    public void run(String... args) {
        try {
            long start = System.currentTimeMillis();
            //SeataServer启动
            Server.start(args);
            started = true;

            long cost = System.currentTimeMillis() - start;
            //记录启动时间
            LOGGER.info("seata server started in {} millSeconds", cost);
        } catch (Throwable e) {
            started = Boolean.FALSE;
            LOGGER.error("seata server start error: {} ", e.getMessage(), e);
            System.exit(-1);
        }
    }

SeataServer启动

public class Server {
    /**
     * The entry point of application.
     *
     * @param args the input arguments
     */
    public static void start(String[] args) {
        // create logger
        final Logger logger = LoggerFactory.getLogger(Server.class);

        //initialize the parameter parser
        //Note that the parameter parser should always be the first line to execute.
        //Because, here we need to parse the parameters needed for startup.
        // 1. 对配置文件做参数解析:包括registry.conf、file.conf的解析
        ParameterParser parameterParser = new ParameterParser(args);

        // 2、初始化监控,做metric指标采集
        MetricsManager.get().init();

        // 将Store资源持久化方式放到系统的环境变量store.mode中
        System.setProperty(ConfigurationKeys.STORE_MODE, parameterParser.getStoreMode());

        // seata server里netty server 的io线程池(核心线程数50,最大线程数100)
        ThreadPoolExecutor workingThreads = new ThreadPoolExecutor(NettyServerConfig.getMinServerPoolSize(),
                NettyServerConfig.getMaxServerPoolSize(),
                NettyServerConfig.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(NettyServerConfig.getMaxTaskQueueSize()),
                new NamedThreadFactory("ServerHandlerThread", NettyServerConfig.getMaxServerPoolSize()), new ThreadPoolExecutor.CallerRunsPolicy());

        // 3、创建TC与RM/TM通信的RPC服务器--netty
        NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads);

        // 4、初始化UUID生成器(雪花算法)
        UUIDGenerator.init(parameterParser.getServerNode());

        //log store mode : file, db, redis
        // 5、设置事务会话的持久化方式,有三种类型可选:file/db/redis
        SessionHolder.init(parameterParser.getSessionStoreMode());
        LockerManagerFactory.init(parameterParser.getLockStoreMode());

        // 6、创建并初始化事务协调器,创建时后台会启动一堆线程
        DefaultCoordinator coordinator = DefaultCoordinator.getInstance(nettyRemotingServer);
        coordinator.init();

        // 将DefaultCoordinator作为Netty Server的transactionMessageHandler;
        // 用于做AT、TCC、SAGA等不同事务类型的逻辑处理
        nettyRemotingServer.setHandler(coordinator);

        // let ServerRunner do destroy instead ShutdownHook, see https://github.com/seata/seata/issues/4028
        // 7、注册ServerRunner销毁(Spring容器销毁)的回调钩子函数
        ServerRunner.addDisposable(coordinator);

        //127.0.0.1 and 0.0.0.0 are not valid here.
        if (NetUtil.isValidIp(parameterParser.getHost(), false)) {
            XID.setIpAddress(parameterParser.getHost());
        } else {
            String preferredNetworks = ConfigurationFactory.getInstance().getConfig(REGISTRY_PREFERED_NETWORKS);
            if (StringUtils.isNotBlank(preferredNetworks)) {
                XID.setIpAddress(NetUtil.getLocalIp(preferredNetworks.split(REGEX_SPLIT_CHAR)));
            } else {
                XID.setIpAddress(NetUtil.getLocalIp());
            }
        }
        // 8、启动netty Server,用于接收TM/RM的请求
        nettyRemotingServer.init();
    }
}

配置文件解析

ParameterParser

    public ParameterParser(String... args) {
        this.init(args);
    }
    private void init(String[] args) {
        try {
            //解析运行期间的参数 默认为啥都没有
            getCommandParameters(args);
            //读取host、port、server节点以及StoreMode存储模式
            getEnvParameters();
            if (StringUtils.isNotBlank(seataEnv)) {
                System.setProperty(ENV_PROPERTY_KEY, seataEnv);
            }
            if (StringUtils.isBlank(storeMode)) {
                storeMode = CONFIG.getConfig(ConfigurationKeys.STORE_MODE, SERVER_DEFAULT_STORE_MODE);
            }
            if (StringUtils.isBlank(sessionStoreMode)) {
                sessionStoreMode = CONFIG.getConfig(ConfigurationKeys.STORE_SESSION_MODE, storeMode);
            }
            if (StringUtils.isBlank(lockStoreMode)) {
                lockStoreMode = CONFIG.getConfig(ConfigurationKeys.STORE_LOCK_MODE, storeMode);
            }
        } catch (ParameterException e) {
            printError(e);
        }

    }
private void getEnvParameters() {
    // 设置seata的环境
    if (StringUtils.isBlank(seataEnv)) {
        seataEnv = ContainerHelper.getEnv();
    }
    // 设置Host
    if (StringUtils.isBlank(host)) {
        host = ContainerHelper.getHost();
    }
    // 设置端口号
    if (port == 0) {
        port = ContainerHelper.getPort();
    }
    if (serverNode == null) {
        serverNode = ContainerHelper.getServerNode();
    }
    if (StringUtils.isBlank(storeMode)) {
        storeMode = ContainerHelper.getStoreMode();
    }
    if (StringUtils.isBlank(sessionStoreMode)) {
        sessionStoreMode = ContainerHelper.getSessionStoreMode();
    }
    if (StringUtils.isBlank(lockStoreMode)) {
        lockStoreMode = ContainerHelper.getLockStoreMode();
    }
}

初始化监控

MetricsManager

    public void init() {
        boolean enabled = ConfigurationFactory.getInstance().getBoolean(
            ConfigurationKeys.METRICS_PREFIX + ConfigurationKeys.METRICS_ENABLED, false);
        if (enabled) {
            registry = RegistryFactory.getInstance();
            if (registry != null) {
                List<Exporter> exporters = ExporterFactory.getInstanceList();
                //only at least one metrics exporter implement had imported in pom then need register MetricsSubscriber
                if (exporters.size() != 0) {
                    exporters.forEach(exporter -> exporter.setRegistry(registry));
                    EventBusManager.get().register(new MetricsSubscriber(registry));
                }
            }
        }
    }

创建和客户端连接通道

这里是基于Netty去构建的

NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads);
    public NettyRemotingServer(ThreadPoolExecutor messageExecutor) {
        super(messageExecutor, new NettyServerConfig());
    }
    public AbstractNettyRemotingServer(ThreadPoolExecutor messageExecutor, NettyServerConfig nettyServerConfig) {
        super(messageExecutor);
        serverBootstrap = new NettyServerBootstrap(nettyServerConfig);
        //设置header 去接受客户端的请求
        serverBootstrap.setChannelHandlers(new ServerHandler());
    }

初始化UUID生成器

UUID底层采用雪花算法,其用于生成全局事务id和分支事务id;

UUIDGenerator.init(parameterParser.getServerNode());

设置事务会话

SessionHolder负责事务会话Session的持久化,一个session对应一个事务,事务又分为全局事务和分支事务;
SessionHolder支持db,file和redis的持久化方式,其中redis和db支持集群模式,项目上推荐使用redis或db模式

 SessionHolder.init(parameterParser.getSessionStoreMode());
// 用于管理所有的Setssion,以及Session的创建、更新、删除等
private static SessionManager ROOT_SESSION_MANAGER;
// 用于管理所有的异步commit的Session,包括创建、更新以及删除
private static SessionManager ASYNC_COMMITTING_SESSION_MANAGER;
// 用于管理所有的重试commit的Session,包括创建、更新以及删除
private static SessionManager RETRY_COMMITTING_SESSION_MANAGER;
// 用于管理所有的重试rollback的Session,包括创建、更新以及删除
private static SessionManager RETRY_ROLLBACKING_SESSION_MANAGER;
// 用于管理分布式锁
private static DistributedLocker DISTRIBUTED_LOCKER;

设置全局锁管理器

        LockerManagerFactory.init(parameterParser.getLockStoreMode());

这里用到了双重检查锁机制

    public static void init(String lockMode) {
        if (LOCK_MANAGER == null) {
            synchronized (LockerManagerFactory.class) {
                if (LOCK_MANAGER == null) {
                    if (StringUtils.isBlank(lockMode)) {
                        lockMode = CONFIG.getConfig(ConfigurationKeys.STORE_LOCK_MODE,
                            CONFIG.getConfig(ConfigurationKeys.STORE_MODE, SERVER_DEFAULT_STORE_MODE));
                    }
                    if (StoreMode.contains(lockMode)) {
                        LOCK_MANAGER = EnhancedServiceLoader.load(LockManager.class, lockMode);
                    }
                }
            }
        }
    }

创建并初始化事务协调器(TC)

DefaultCoordinator是事务协调的核心,比如:开启、提交、回滚全局事务,注册、提交、回滚分支事务都是通过DefaultCoordinator进行协调处理的。

        DefaultCoordinator coordinator = DefaultCoordinator.getInstance(nettyRemotingServer);
        coordinator.init();

创建事务协调器(TC)

双重检查锁确保这是单例的

    public static DefaultCoordinator getInstance(RemotingServer remotingServer) {
        if (null == instance) {
            synchronized (DefaultCoordinator.class) {
                if (null == instance) {
                    instance = new DefaultCoordinator(remotingServer);
                }
            }
        }
        return instance;
    }
    private DefaultCoordinator(RemotingServer remotingServer) {
        if (remotingServer == null) {
            throw new IllegalArgumentException("RemotingServer not allowed be null.");
        }
        this.remotingServer = remotingServer;
        this.core = new DefaultCore(remotingServer);
    }
    public DefaultCore(RemotingServer remotingServer) {
        List<AbstractCore> allCore = EnhancedServiceLoader.loadAll(AbstractCore.class,
            new Class[] {RemotingServer.class}, new Object[] {remotingServer});
        if (CollectionUtils.isNotEmpty(allCore)) {
            for (AbstractCore core : allCore) {
                // coreMap 代表 seata事务的四种模式  AT、TCC、XA、Saga
                coreMap.put(core.getHandleBranchType(), core);
            }
        }
    }

初始化事务协调器(TC)

除定时清理undo_log外,其余定时任务的处理逻辑基本都是:
首先获取所有可回滚的全局事务会话Session,如果可回滚的分支事务为空,则直接返回;
否者,遍历所有的可回滚Session;为了防止重复回滚,如果session的状态是正在回滚中并且session不是死亡的,则直接返回;
如果Session重试回滚超时,从缓存中删除已经超时的回滚Session;
发布session回滚完成事件给到Metric,对回滚中的Session添加Session生命周期的监听;
使用DefaultCoordinator组合的DefaultCore执行全局回滚。

    public void init() {
        //处理重试回滚
        retryRollbacking.scheduleAtFixedRate(
            () -> SessionHolder.distributedLockAndExecute(RETRY_ROLLBACKING, this::handleRetryRollbacking), 0,
            ROLLBACKING_RETRY_PERIOD, TimeUnit.MILLISECONDS);
        //处理重试提交
        retryCommitting.scheduleAtFixedRate(
            () -> SessionHolder.distributedLockAndExecute(RETRY_COMMITTING, this::handleRetryCommitting), 0,
            COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS);
        //处理异步提交
        asyncCommitting.scheduleAtFixedRate(
            () -> SessionHolder.distributedLockAndExecute(ASYNC_COMMITTING, this::handleAsyncCommitting), 0,
            ASYNC_COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS);
       //做超时检查  
        timeoutCheck.scheduleAtFixedRate(
            () -> SessionHolder.distributedLockAndExecute(TX_TIMEOUT_CHECK, this::timeoutCheck), 0,
            TIMEOUT_RETRY_PERIOD, TimeUnit.MILLISECONDS);
       //undo——log  日志的删除
        undoLogDelete.scheduleAtFixedRate(
            () -> SessionHolder.distributedLockAndExecute(UNDOLOG_DELETE, this::undoLogDelete),
            UNDO_LOG_DELAY_DELETE_PERIOD, UNDO_LOG_DELETE_PERIOD, TimeUnit.MILLISECONDS);
    }

注册销毁ServerRunner的钩子方法

   ServerRunner.addDisposable(coordinator);

初始化NettyServer处理客户端的请求

        nettyRemotingServer.init();
    @Override
    public void init() {
        // 注册处理器
        registerProcessor();
        if (initialized.compareAndSet(false, true)) {
            //没有初始化进行初始化
            super.init();
        }
    }

注册处理器

消息处理器是用来处理消息的,其根据消息的不同类型选择不同的消息处理器来处理消息(属于典型的策略模式)

private void registerProcessor() {
        // 1. registry on request message processor
        ServerOnRequestProcessor onRequestProcessor =
            new ServerOnRequestProcessor(this, getHandler());
        ShutdownHook.getInstance().addDisposable(onRequestProcessor);
        super.registerProcessor(MessageType.TYPE_BRANCH_REGISTER, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_BRANCH_STATUS_REPORT, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_GLOBAL_BEGIN, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_GLOBAL_COMMIT, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_GLOBAL_LOCK_QUERY, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_GLOBAL_REPORT, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_GLOBAL_ROLLBACK, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_GLOBAL_STATUS, onRequestProcessor, messageExecutor);
        super.registerProcessor(MessageType.TYPE_SEATA_MERGE, onRequestProcessor, messageExecutor);
        // 2. registry on response message processor
        ServerOnResponseProcessor onResponseProcessor =
            new ServerOnResponseProcessor(getHandler(), getFutures());
        super.registerProcessor(MessageType.TYPE_BRANCH_COMMIT_RESULT, onResponseProcessor, branchResultMessageExecutor);
        super.registerProcessor(MessageType.TYPE_BRANCH_ROLLBACK_RESULT, onResponseProcessor, branchResultMessageExecutor);
        // 3. registry rm message processor
        RegRmProcessor regRmProcessor = new RegRmProcessor(this);
        super.registerProcessor(MessageType.TYPE_REG_RM, regRmProcessor, messageExecutor);
        // 4. registry tm message processor
        RegTmProcessor regTmProcessor = new RegTmProcessor(this);
        super.registerProcessor(MessageType.TYPE_REG_CLT, regTmProcessor, null);
        // 5. registry heartbeat message processor
        ServerHeartbeatProcessor heartbeatMessageProcessor = new ServerHeartbeatProcessor(this);
        super.registerProcessor(MessageType.TYPE_HEARTBEAT_MSG, heartbeatMessageProcessor, null);
    }

所谓的注册消息处理器本质上就是将处理器RemotingProcessor和处理消息的线程池ExecutorService包装成一个Pair,然后将Pair作为Value,messageType作为key放入一个Map(processorTable)中;

    @Override
    public void registerProcessor(int messageType, RemotingProcessor processor, ExecutorService executor) {
        Pair<RemotingProcessor, ExecutorService> pair = new Pair<>(processor, executor);
        this.processorTable.put(messageType, pair);
    }

在这里插入图片描述

SeataNettyServer初始化

init方法的作用每3秒遍历futures,查询过期的MessageFuture,并将其从futures中清除。

    public void init() {
        timerExecutor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
            //遍历消息futures,处理过期的消息
                for (Map.Entry<Integer, MessageFuture> entry : futures.entrySet()) {
                    MessageFuture future = entry.getValue();
                    if (future.isTimeout()) {
                        futures.remove(entry.getKey());
                        RpcMessage rpcMessage = future.getRequestMessage();
                        future.setResultMessage(new TimeoutException(String
                            .format("msgId: %s ,msgType: %s ,msg: %s ,request timeout", rpcMessage.getId(), String.valueOf(rpcMessage.getMessageType()), rpcMessage.getBody().toString())));
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug("timeout clear future: {}", entry.getValue().getRequestMessage().getBody());
                        }
                    }
                }

                nowMills = System.currentTimeMillis();
            }
        }, TIMEOUT_CHECK_INTERVAL, TIMEOUT_CHECK_INTERVAL, TimeUnit.MILLISECONDS);
    }

SeataNettyServer启动

这里其实就是启动的Netty去处理和客户端的交互 当有消息到来时会走到 ServerHandler的channelRead方法中去进行消息的处理

    @Override
    public void start() {
        this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupWorker)
            .channel(NettyServerConfig.SERVER_CHANNEL_CLAZZ)
            .option(ChannelOption.SO_BACKLOG, nettyServerConfig.getSoBackLogSize())
            .option(ChannelOption.SO_REUSEADDR, true)
            .childOption(ChannelOption.SO_KEEPALIVE, true)
            .childOption(ChannelOption.TCP_NODELAY, true)
            .childOption(ChannelOption.SO_SNDBUF, nettyServerConfig.getServerSocketSendBufSize())
            .childOption(ChannelOption.SO_RCVBUF, nettyServerConfig.getServerSocketResvBufSize())
            .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK,
                new WriteBufferWaterMark(nettyServerConfig.getWriteBufferLowWaterMark(),
                    nettyServerConfig.getWriteBufferHighWaterMark()))
            .localAddress(new InetSocketAddress(getListenPort()))
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) {
                    ch.pipeline().addLast(new IdleStateHandler(nettyServerConfig.getChannelMaxReadIdleSeconds(), 0, 0))
                        .addLast(new ProtocolV1Decoder())
                        .addLast(new ProtocolV1Encoder());
                    if (channelHandlers != null) {
                        addChannelPipelineLast(ch, channelHandlers);
                    }

                }
            });

        try {
            this.serverBootstrap.bind(getListenPort()).sync();
            XID.setPort(getListenPort());
            LOGGER.info("Server started, service listen port: {}", getListenPort());
            RegistryFactory.getInstance().register(new InetSocketAddress(XID.getIpAddress(), XID.getPort()));
            initialized.set(true);
        } catch (SocketException se) {
            throw new RuntimeException("Server start failed, the listen port: " + getListenPort(), se);
        } catch (Exception exx) {
            throw new RuntimeException("Server start failed", exx);
        }
    }

总结

1.解析registry.conf、file.conf等配置文件
2.创建一个监控,做metric指标采集
3.创建SeataNettyServer服务端(TC事务协调器) 并设置一个Handler用来处理AT、TCC、SAGA等不同事务类型的逻辑处理
4.设置事务持久化方式
5.设置全局锁的管理器
6.启动Seata服务端 建立和客户端的通道 默认端口为8091

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

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

相关文章

“机器人V2.0时代已来”-任务规划难题迎刃而解,世界因机器人改变而翻转!

01-VILA背景简介 2022年&#xff0c;Michael Ahn, Anthony Brohan等人提出“Do as i can, not as i say: Grounding language in robotic affordances”算法。本文指出虽然大型语言模型可以编码关于世界的丰富语义知识&#xff0c;而这些知识对旨在对用自然语言表达的高级、时…

系统的安全性设计

要设计一个安全的系统&#xff0c;除了要了解一些前面讲到的常用的保护手段和技术措施外&#xff0c;还要对系统中可能出现的安全问题或存在的安全隐患有充分的认识&#xff0c;这样才能对系统的安全作有针对性的设计和强化&#xff0c;即“知己知彼&#xff0c;百战百胜”。 下…

【超图】SuperMap iClient3D for WebGL/WebGPU ——暴雪

作者&#xff1a;taco 时隔多年北京又开始降下了特大暴雪。身为打工人的你有没有居家办公呢&#xff1f;反正小编我是没有。既然没有借着暴雪的功劳居家办公&#xff0c;那就接着雪来输出一篇博客好了。基于SuperMap iClient3D for WebGL/WebGPU 实现暴雪仿真效果。 先来看下效…

三年没回家过年,你的羽绒服准备好了吗?

2023进入尾声&#xff0c;这一年&#xff0c;我们在“阳康”之后重新出发&#xff0c;找回了烟火气&#xff0c;和错过几年的山水重逢&#xff0c;向往远方的同时也更爱眼前的点滴&#xff0c;逐渐重建起对美好生活的期待。 把生活态度投射到社媒上&#xff0c;出圈的热点话题…

设置一个vue文件的全局模板

VsCode在新建一个.vue文件的时候是空白的&#xff0c;需要我们自己输入片段&#xff0c;可这些在每次新建.vue文件都需要自己手敲&#xff0c;所以创建一个模板方便使用 设置vue模板 导入 {"生成 vue 模板": {"prefix": "vue","body"…

【每日一题】反转二叉树的奇数层

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;广度优先搜索方法二&#xff1a;深度优先搜索 写在最后 Tag 【深度优先搜索】【广度优先搜索】【二叉树】【2023-12-15】 题目来源 2415. 反转二叉树的奇数层 题目解读 反转二叉树奇数层的节点。 解题思路 对于二叉…

文献管理器Zotero使用WebDAV结合内网穿透实现公网环境跨平台同步文献笔记

文章目录 一、Zotero安装教程二、群晖NAS WebDAV设置三、Zotero设置四、使用公网地址同步Zotero文献库五、使用永久固定公网地址同步Zotero文献库 Zotero 是一款全能型 文献管理器,可以 存储、管理和引用文献&#xff0c;不但免费&#xff0c;功能还很强大实用。 ​ Zotero 支…

MySQL - 创建表的三种方法详解及练习

目录 &#x1f959;1. 基础创建 &#x1f9c0;实例1 &#x1f959;2. 带约束创建 &#x1f9c0;实例2 &#x1f959;3. 复制创建 &#x1f9c0;实例3&#xff1a; &#x1f9c0;实例4&#xff1a; &#x1f9c0;实例5&#xff1a; ​ &#x1f9c0;实例6&#xff1a; &am…

【华为数据之道学习笔记】5-1支撑非数字原生企业数字化转型的数据底座建设框架

华为通过建设数据底座&#xff0c;将公司内外部的数据汇聚在一起&#xff0c;对数据进行重新组织和联接&#xff0c;让数据有清晰的定义和统一的结构&#xff0c;并在尊重数据安全与隐私的前提下&#xff0c;让数据更易获取&#xff0c;最终打破数据孤岛和垄断。通过数据底座&a…

MES生产管理系统和APS排程系统具体有什么区别

在当今制造业的复杂环境中&#xff0c;制造企业面临着提高生产效率、降低成本并提高客户满意度的挑战。为了应对这些挑战&#xff0c;许多企业开始依赖于各种先进的信息技术系统。其中&#xff0c;MES生产管理系统和APS排程系统是两个关键的系统&#xff0c;它们在生产管理中发…

了解linux的ansible 的使用

本章主要介绍在RHEL8中如何安装ansible 及 ansible 的基本使用。 ansible是如何工作的 在 RHEL8中安装ansible 编写ansible.cfg和清单文件 ansible 的基本用法 如果管理的服务器很多&#xff0c;如几十台甚至几百台&#xff0c;那么就需要一个自动化管理工具了&#xff0c; a…

ac转dc电源芯片SM7025 支持12V/18V输出电压

AC转DC电源芯片是一种能够将交流电转换为直流电的重要器件&#xff0c;广泛应用于电子设备和电源系统中。它可以提供稳定的直流电源&#xff0c;为设备的正常运行提供保障。 AC转DC电源芯片的工作原理是利用内部的整流、滤波、变压器和稳压等电路&#xff0c;将输入的交流电转换…

qt-C++笔记之addAction和addMenu的区别以及QAction的使用场景

qt-C笔记之addAction和addMenu的区别以及QAction的使用场景 code review! 文章目录 qt-C笔记之addAction和addMenu的区别以及QAction的使用场景1.QMenu和QMenuBar的关系与区别2.addMenu和addAction的使用场景区别3.将QAction的信号连接到槽函数4.QAction的使用场景5.将例1修改…

用串口给stm32下载程序

由于开发板没有预留swd下载口&#xff0c;于时在网上找教程用串口给开发板下载。 记录一下。 好文推荐&#xff1a; 如何使用串口来给STM32下载程序 - 知乎 (zhihu.com) 驱动安装&#xff1a; CH340&#xff1a;CH340/CH341USB转串口WINDOWS驱动程序 PL2302&#xff1a;…

【论文解读】ICLR 2024高分作:ViT需要寄存器

来源&#xff1a;投稿 作者&#xff1a;橡皮 编辑&#xff1a;学姐 论文链接&#xff1a;https://arxiv.org/abs/2309.16588 摘要&#xff1a; Transformer最近已成为学习视觉表示的强大工具。在本文中&#xff0c;我们识别并表征监督和自监督 ViT 网络的特征图中的伪影。这些…

Axure的动态面板的使用

目录 1.什么是动态面板&#xff1f; 2.使用动态面板 ​编辑 轮播图 erp的登录系统 erp侧边栏 1.什么是动态面板&#xff1f; 动态面板是Axure的高级交互元件&#xff0c;由不同的状态面板组成&#xff0c;是我们制作交互过程中运用频率最高的元件&#xff0c;很多交互效果需…

Intewell-Hyper I_V2.0.0_release版本正式发布

新型工业操作系统_Intewell-Hyper I_V2.0.0_release版本正式发布 软件发布版本信息 版本号&#xff1a;V2.0.0 版本发布类型&#xff1a;release正式版本 版本特点 1.建立Intewell-Hyper I基线版本 版本或修改说明 基于Intewell-Lin V2.3.0_release版本&#xff1a; 1.Devel…

GaussDB如何创建和管理视图

GaussDB如何创建和管理视图 一、什么是视图 当用户对数据库中的一张或者多张表的某些字段的组合感兴趣&#xff0c;而又不想每次键入这些查询时&#xff0c;用户就可以定义一个视图&#xff0c;以便解决这个问题。 视图与基本表不同&#xff0c;不是物理上实际存在的&#x…

LeetCode刷题--- 二叉树的所有路径

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归算法题 【 http://t.csdnimg.cn/yUl2I 】 【C】 【 http://t.csdnimg.cn/6AbpV 】 数据结构与算法 【 http://t.csdnimg.cn/hKh2l 】 前言&…

云原生向量计算引擎 PieCloudVector:为大模型提供独特记忆

拓数派大模型数据计算系统&#xff08;PieDataComputingSystem&#xff0c;缩写&#xff1a;πDataCS&#xff09;在10月24日程序员节「大模型数据计算系统」2023拓数派年度技术论坛正式发布。πDataCS 以云原生技术重构数据存储和计算&#xff0c;「一份存储&#xff0c;多引擎…