22.RocketMQ之NameServer启动流程

news2024/11/16 0:03:35

NameServerController启动流程总览

启动类:org.apache.rocketmq.namesrv.NamesrvStartup#main ```java public static void main(String[] args) { main0(args); }

```

java public static NamesrvController main0(String[] args) { try { //创建NamesrvController NamesrvController controller = createNamesrvController(args); //初始化并启动NamesrvController start(controller); String tip = "The Name Server boot success. serializeType=" + RemotingCommand.getSerializeTypeConfigInThisServer(); log.info(tip); System.out.printf("%s%n", tip); return controller; } catch (Throwable e) { e.printStackTrace(); System.exit(-1); } return null; }

1.创建NamesrvController

```java public static NamesrvController createNamesrvController(String[] args) throws IOException, JoranException { //设置MQ版本号 System.setProperty(RemotingCommand.REMOTINGVERSIONKEY, Integer.toString(MQVersion.CURRENT_VERSION));

//解析启动命令 start mqnamesrv.cmd
Options options = ServerUtil.buildCommandlineOptions(new Options());
commandLine = ServerUtil.parseCmdLine
        ("mqnamesrv",
         args, 
         buildCommandlineOptions(options), 
         new PosixParser());

if (null == commandLine) {
    System.exit(-1);
    return null;
}

​ //创建NamesrvConfig final NamesrvConfig namesrvConfig = new NamesrvConfig(); //创建NettyServerConfig final NettyServerConfig nettyServerConfig = new NettyServerConfig(); //设置启动端口号9876 nettyServerConfig.setListenPort(9876); //解析启动-c参数 if (commandLine.hasOption('c')) { //-c指定配置文件 String file = commandLine.getOptionValue('c'); if (file != null) { //加载配置文件到流 InputStream in = new BufferedInputStream (new FileInputStream(file)); //加载属性到InputStream properties = new Properties(); properties.load(in); //分别设置属性到namesrvConfig 和 nettyServerConfig   MixAll.properties2Object(properties, namesrvConfig); MixAll.properties2Object(properties, nettyServerConfig); //设置配置文件存储地址 namesrvConfig.setConfigStorePath(file); System.out.printf("load config properties file OK, %s%n", file); in.close(); } } ​ //-p来指定是否打印配置项,在指定该选项时,直接退出。 if (commandLine.hasOption('p')) { InternalLogger console = InternalLoggerFactory.getLogger (LoggerName.NAMESRVCONSOLENAME); //打印namesrvConfig属性 MixAll.printObjectProperties(console, namesrvConfig); //打印nettyServerConfig 属性 MixAll.printObjectProperties(console, nettyServerConfig); System.exit(0); } ​ //将启动参数填充到namesrvConfig,nettyServerConfig MixAll.properties2Object (ServerUtil.commandLine2Properties(commandLine), namesrvConfig); ​ //创建NameServerController final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);

controller.getConfiguration().registerConfig(properties);
return controller;

} ```

2.初始化NamesrvController

3.启动NamesrvController

```java public static NamesrvController start(final NamesrvController controller) throws Exception {

if (null == controller) {
        throw new IllegalArgumentException("NamesrvController is null");
    }

    boolean initResult = controller.initialize();
    if (!initResult) {
        controller.shutdown();
        System.exit(-3);
    }
    //注册JVM钩子函数代码
    //在JVM进程关闭之前,先将线程池关闭,及时释放资源
    //可以借鉴的地方
    Runtime.getRuntime()
        .addShutdownHook(new ShutdownHookThread(log, new Callable<Void>() {
        @Override
        public Void call() throws Exception {
            //释放资源
            controller.shutdown();
            return null;
        }
    }));
    controller.start();
    return controller;
}

public void shutdown() { //关闭nettyServer this.remotingServer.shutdown(); //关闭线程池 this.remotingExecutor.shutdown(); //关闭定时任务 this.scheduledExecutorService.shutdown(); //功能实现当文件内容发生变化时,重新加载文件,可用于读取配置类的文件。 //原理:注册一个listener,然后新开个线程,定期去扫描文件 //通过对文件内容进行hash来判断文件内容是否发生变化 //如果变化了,则回调监听器的onChange方法。 //看源码主要是监听证书 //关闭fileWatchService if (this.fileWatchService != null) { this.fileWatchService.shutdown(); } } ```

大致流程如下图

image.png

上面对源码做了概览,大致知道了NameServerController启动的流程分为3步,创建-初始化-启动。

下面一步一步看吧。

1.创建nameServerController

1.1:解析配置文件,创建NameSrvController

解析配置文件,填充NameServerConfig、NettyServerConfig属性值,并创建NamesrvController

注意NameServer创建的是是NettyServerConfig,Broker创建的是NettyClientConfig

NamesrvStartup#createNamesrvController

```java public static NamesrvController createNamesrvController(String[] args) throws IOException, JoranException { //设置MQ版本号 System.setProperty(RemotingCommand.REMOTINGVERSIONKEY, Integer.toString(MQVersion.CURRENT_VERSION));

//解析启动命令 start mqnamesrv.cmd
Options options = ServerUtil.buildCommandlineOptions(new Options());
commandLine = ServerUtil.parseCmdLine
        ("mqnamesrv",
         args, 
         buildCommandlineOptions(options), 
         new PosixParser());

if (null == commandLine) {
    System.exit(-1);
    return null;
}

​ //创建NamesrvConfig final NamesrvConfig namesrvConfig = new NamesrvConfig(); //创建NettyServerConfig final NettyServerConfig nettyServerConfig = new NettyServerConfig(); //设置启动端口号9876 nettyServerConfig.setListenPort(9876); //解析启动-c参数 if (commandLine.hasOption('c')) { //-c指定配置文件 String file = commandLine.getOptionValue('c'); if (file != null) { //加载配置文件到流 InputStream in = new BufferedInputStream (new FileInputStream(file)); //加载属性到InputStream properties = new Properties(); properties.load(in); //分别设置属性到namesrvConfig 和 nettyServerConfig   MixAll.properties2Object(properties, namesrvConfig); MixAll.properties2Object(properties, nettyServerConfig); //设置配置文件存储地址 namesrvConfig.setConfigStorePath(file); System.out.printf("load config properties file OK, %s%n", file); in.close(); } } ​ //-p来指定是否打印配置项,在指定该选项时,直接退出。 if (commandLine.hasOption('p')) { InternalLogger console = InternalLoggerFactory.getLogger (LoggerName.NAMESRVCONSOLENAME); //打印namesrvConfig属性 MixAll.printObjectProperties(console, namesrvConfig); //打印nettyServerConfig 属性 MixAll.printObjectProperties(console, nettyServerConfig); System.exit(0); } ​ //将启动参数填充到namesrvConfig,nettyServerConfig MixAll.properties2Object (ServerUtil.commandLine2Properties(commandLine), namesrvConfig); ​ //创建NameServerController final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);

controller.getConfiguration().registerConfig(properties);
return controller;

} ```

2.初始化nameServerController

2.1:初始化NamesrvController

根据启动属性创建NamesrvController实例,并初始化该实例。

NameServerController实例为NameServer核心控制器。

NamesrvController#initialize

```java public boolean initialize() {

//加载KV配置
this.kvConfigManager.load();

/***
    这里需要看NettyRemotingServer构造方法
    会把netty的启动辅助类serverBootstrap创建好,这个是重点
    保存了channelEventListener。
    新建了netty的boss线程。
    创建publicExecutor线程池。
***/
this.remotingServer = new NettyRemotingServer
                        (this.nettyServerConfig, this.brokerHousekeepingService);

//创建线程池 默认是8个
this.remotingExecutor =
    Executors.newFixedThreadPool
    (nettyServerConfig.getServerWorkerThreads(),  
                        new ThreadFactoryImpl("RemotingExecutorThread_"));

/***
创建DefaultRequestProcessor 作为netty server 请求处理器。
org.apache.rocketmq.namesrv.processor.DefaultRequestProcessor#processRequest 
处理所有已知request code类型的请求
**/
this.registerProcessor();

//开启定时任务:每隔10s扫描一次Broker,移除不活跃的Broker
//如果在2分钟都没有发送心跳 移除不活跃的Broker
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        NamesrvController.this.routeInfoManager.scanNotActiveBroker();
    }
}, 5, 10, TimeUnit.SECONDS);

//开启定时任务:每隔10min打印一次KV配置
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        NamesrvController.this.kvConfigManager.printAllPeriodically();
    }
}, 1, 10, TimeUnit.MINUTES);
return true;

} ```

2.2:启动定时任务:每10秒扫描一次所有broker

Broker30秒向NameServer发送一次心跳。

NamesrvController会开启定时任务:每隔10s扫描一次Broker,移除不活跃的Broker。

移除broker是根据broker的lastUpdateStamp+2分钟是否小于当前时间,如果小于就移除。

如果某个broker在2分钟内都没有发送心跳 那么就移除该broker 即连续4次没有发送心跳就移除

RouteInfoManager#scanNotActiveBroker

```java //扫描不活跃的broker public void scanNotActiveBroker() { //2分钟 private final static long BROKERCHANNELEXPIRED_TIME = 1000 * 60 * 2;

//HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable;
Iterator<Entry<String, BrokerLiveInfo>> it 
                        = this.brokerLiveTable.entrySet().iterator();
while (it.hasNext()) {

    Entry<String, BrokerLiveInfo> next = it.next();
    //BrokerLiveInfo中的lastUpdateTimestamp存储上次收到Broker心跳包的时间。
    long last = next.getValue().getLastUpdateTimestamp();
    //BrokerLiveInfo中的 
   //lastUpdateTimestamp+2分钟小于当前时间说明 已经2分钟没有心跳了
    if ((last + BROKER_CHANNEL_EXPIRED_TIME) < System.currentTimeMillis()) {
        //关闭并移除channel
        RemotingUtil.closeChannel(next.getValue().getChannel());
        it.remove();
        //销毁channel工作
        this.onChannelDestroy(next.getKey(), 
                                next.getValue().getChannel());
    }
}

} ```

移除2分钟没心跳的broker的路由元信息:RouteInfoManager#onChannelDestroy

java //路由元信息 //类:RouteInfoManager private final HashMap<String/* topic */, List<QueueData>> topicQueueTable; private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable; private final HashMap<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable; private final HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable; private final HashMap<String/* brokerAddr */, List<String>/* Filter Server */> filterServerTable;

topicQueueTable: Topic消息队列路由信息,消息发送时根据路由表进行负载均衡

brokerAddrTable: Broker基础信息,包括brokerName、所属集群名称、主备Broker地址

clusterAddrTable: Broker集群信息,存储集群中所有Broker名称

brokerLiveTable: Broker状态信息,NameServer每次收到心跳包是会替换该信息

filterServerTable: Broker上的FilterServer列表,用于类模式消息过滤。

一个Topic拥有多个消息队列,一个Broker为每一个主题创建8个读队列和8个写队列。

多个Broker组成一个集群,集群由相同的多台Broker组成Master-Slave架构。

brokerId为0代表Master,大于0为Slave。

BrokerLiveInfo中的lastUpdateTimestamp存储上次收到Broker心跳包的时间。

image.png

image.png

``` //主要就是移除路由信息表相关信息 public void onChannelDestroy(String remoteAddr, Channel channel) { String brokerAddrFound = null; if (channel != null) { try { try { //申请写锁,根据brokerAddress //从brokerLiveTable和filterServerTable移除 this.lock.readLock().lockInterruptibly(); Iterator > itBrokerLiveTable = this.brokerLiveTable.entrySet().iterator(); while (itBrokerLiveTable.hasNext()) { Entry entry = itBrokerLiveTable.next(); if (entry.getValue().getChannel() == channel) { brokerAddrFound = entry.getKey(); break; } } } finally { this.lock.readLock().unlock(); } } catch (Exception e) { log.error("onChannelDestroy Exception", e); } } ​ if (null == brokerAddrFound) { brokerAddrFound = remoteAddr; } else { log.info("the broker's channel destroyed" + "clean it's data structure at once"); } ​ if (brokerAddrFound != null && brokerAddrFound.length() > 0) { ​ try { try { this.lock.writeLock().lockInterruptibly(); this.brokerLiveTable.remove(brokerAddrFound); this.filterServerTable.remove(brokerAddrFound); String brokerNameFound = null; boolean removeBrokerName = false; ​ //维护 brokerAddrTable Iterator > itBrokerAddrTable = this.brokerAddrTable.entrySet().iterator();

//遍历brokerAddrTable
          while (itBrokerAddrTable.hasNext() && (null == brokerNameFound)) {
                //获取brokerData
                BrokerData brokerData = itBrokerAddrTable.next().getValue();
                //遍历该broker的所有地址 即主从
                Iterator<Entry<Long, String>> it
                        = brokerData.getBrokerAddrs().entrySet().iterator();
                //循环遍历主从
                while (it.hasNext()) {
                    Entry<Long, String> entry = it.next();
                    Long brokerId = entry.getKey();
                    String brokerAddr = entry.getValue();
                     //根据broker地址移除brokerAddr
                    if (brokerAddr.equals(brokerAddrFound)) {
                        brokerNameFound = brokerData.getBrokerName();
                        it.remove();
                        break;
                    }
                }
                //如果移除以后没有其他的BrokerAddr 相当于这个broker已经没有实例了
                //那么把brokerData也从BrokerAddrTable 移除
               // <String/* brokerName */, BrokerData> brokerAddrTable
                if (brokerData.getBrokerAddrs().isEmpty()) {
                    removeBrokerName = true;
                    itBrokerAddrTable.remove();
                }
            }

            /***
               维护集群信息: key = clusterName value对应的set是 brokerName 
               <String, Set<String>> clusterAddrTable
               这里移除的条件是 removeBrokerName=true
               removeBrokerName 是在移除brokerAddr时 当braokerData中的addrs为空
               即该broker的主从都不存在 这个broker已经没有实例了
               设置removeBrokerName=true
           ***/
            if (brokerNameFound != null && removeBrokerName) {
                Iterator<Entry<String, Set<String>>> 
                        it = this.clusterAddrTable.entrySet().iterator();
                //遍历clusterAddrTable
                while (it.hasNext()) {
                    Entry<String, Set<String>> entry = it.next();
                      //获得集群名称
                    String clusterName = entry.getKey();
                     //获得集群中brokerName集合
                    Set<String> brokerNames = entry.getValue();
                    //从brokerNames中移除brokerNameFound
                    boolean removed = brokerNames.remove(brokerNameFound);
                    if (removed) {
                        if (brokerNames.isEmpty()) {
                            //如果集群中不包含任何broker,则移除该集群
                            it.remove();
                        }
                        break;
                    }
                }
            }

            //<String/* topic */, List<QueueData>> topicQueueTable队列
            //这里移除的条件是 removeBrokerName=true
            //removeBrokerName 是在移除brokerAddr时 当brokerData中的addrs为空
            //即该broker的主从都不存在,这个broker已经没有实例了
            //设置removeBrokerName=true
            if (removeBrokerName) {
                Iterator<Entry<String, List<QueueData>>> itTopicQueueTable =
                    this.topicQueueTable.entrySet().iterator();
                //遍历topicQueueTable
                while (itTopicQueueTable.hasNext()) {
                    Entry<String, List<QueueData>> entry
                                        = itTopicQueueTable.next();
                    //主题名称
                    String topic = entry.getKey();
                    //队列集合
                    List<QueueData> queueDataList = entry.getValue();
                    //遍历该主题队列
                    Iterator<QueueData> itQueueData
                                    = queueDataList.iterator();

                    while (itQueueData.hasNext()) {
                        //获取queueData
                        QueueData queueData = itQueueData.next();
                        //如果queueData中的brokerName等于本次移除的brokerName
                        //那么从队列中移除该queue
                        if (queueData.getBrokerName()
                                    .equals(brokerNameFound)) {
                            itQueueData.remove();
                        }
                    }
                    //如果该topic的队列为空,则移除该topic
                    if (queueDataList.isEmpty()) {
                        itTopicQueueTable.remove();
                    }
                }
            }
        } finally {
            this.lock.writeLock().unlock();
        }
    } catch (Exception e) {
        log.error("onChannelDestroy Exception", e);
    }
}

} ```

3.注册jvm钩子函数,启动NameServerController

3.1注册jvm钩子函数,启动NameSrvCtr

在JVM进程关闭之前,先将线程池关闭,及时释放资源

NamesrvStartup#start

java public static NamesrvController start(final NamesrvController controller) throws Exception { ​ if (null == controller) { throw new IllegalArgumentException("NamesrvController is null"); } ​ boolean initResult = controller.initialize(); if (!initResult) { controller.shutdown(); System.exit(-3); } //注册JVM钩子函数代码 //在JVM进程关闭之前,先将线程池关闭,及时释放资源 //可以借鉴的地方 Runtime.getRuntime() .addShutdownHook(new ShutdownHookThread(log, new Callable<Void>() { @Override public Void call() throws Exception { //释放资源 controller.shutdown(); return null; } })); controller.start(); return controller; } ​ public void shutdown() { //关闭nettyServer this.remotingServer.shutdown(); //关闭线程池 this.remotingExecutor.shutdown(); //关闭定时任务 this.scheduledExecutorService.shutdown(); //功能实现当文件内容发生变化时,重新加载文件,可用于读取配置类的文件。 //原理:注册一个listener,然后新开个线程,定期去扫描文件 //通过对文件内容进行hash来判断文件内容是否发生变化 //如果变化了,则回调监听器的onChange方法。 //看源码主要是监听证书 //关闭fileWatchService if (this.fileWatchService != null) { this.fileWatchService.shutdown(); } }

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

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

相关文章

因Spring与SpringMVC配置信息写反导致Spring无法自动托管对象实例

因Spring与SpringMVC配置信息写反导致Spring无法自动托管对象实例 异常提示 03-Jul-2023 11:25:24.491 警告 [RMI TCP Connection(3)-127.0.0.1] org.springframework.context.support.AbstractApplicationContext.refresh Exception encountered during context initializat…

【485. 最大连续 1 的个数】

目录 一、题目解析二、算法思路三、代码实现 一、题目解析 二、算法思路 三、代码实现 class Solution { public:int findMaxConsecutiveOnes(vector<int>& nums) {int ret0;int left0,right0;for(;right<nums.size();right){if(nums[right]!1){retmax(ret,right…

SQL高级教程

SQL TOP 子句 TOP 子句 TOP 子句用于规定要返回的记录的数目。 对于拥有数千条记录的大型表来说&#xff0c;TOP 子句是非常有用的。 注释&#xff1a;并非所有的数据库系统都支持 TOP 子句。 SQL Server 的语法&#xff1a; SELECT TOP number|percent column_name(s) F…

BOSHIDA DC电源模块在自动化设备中的应用

BOSHIDA DC电源模块在自动化设备中的应用 DC电源模块是一种用于提供电源的设备&#xff0c;可以将交流电转换为直流电&#xff0c;并提供稳定、可靠的电源输出。在自动化设备中&#xff0c;DC电源模块常用于驱动直流电机、控制电磁阀等各种设备。以下是DC电源模块在自动化设备…

初学Spring boot (四) JSR303数据校验及多环境切换

学习回顾&#xff1a;初学Spring boot &#xff08;三&#xff09; yaml配置注入 一、JSR303数据校验 1、先看看如何使用 Springboot中可以用validated来校验数据&#xff0c;如果数据异常则会统一抛出异常&#xff0c;方便异常中心统一处理。我们这里来写个注解让我们的name只…

MySQL数据库第二课----------认识简单命令-----悄悄的变大牛

作者前言 欢迎小可爱们前来借鉴我的gtiee秦老大大 (qin-laoda) - Gitee.com ——————————————————     ———————————— 目录 操作系统 桌面操作系统 服务器操作系统 嵌入式操作系统 移动设备操作系统 常用 Linux 命令的基本使用 为什么要学…

一个11g RAC到单机dg数据不同步问题

客户通过监控发现&#xff0c;主库dest_id2&#xff08;主库往dg库传输&#xff09;归档错误&#xff0c;归档无法从主库传到dg库。 登录主库&#xff0c;查询dest_id2的error&#xff0c;报错提示主库两个节点都无法登录到备库。 以前也遇到过这种情况&#xff0c;一般从主库复…

系统的灵活所在——扩展性

什么是扩展性&#xff1f; 在保持软件不变的情况下&#xff0c;计算机系统性能可以随系统规模扩充而提高的特性。它以添加新功能或修改完善现有功能来考虑软件的未来成长。可扩展性是软件拓展系统的能力。 我们都知道企业在选型时会从多个方面进行综合评估&#xff0c;主要包括…

Python入门教程+项目实战-14.5节-函数装饰器

目录 14.5.1 理解函数装饰器 14.5.2 理解闭包函数 14.5.3 使用闭包进行功能扩展 14.5.4 更优雅的做法&#xff1a;装饰器语法糖 14.5.5 知识要点 14.5.6 系统学习python 14.5.1 理解函数装饰器 在进入正题前&#xff0c;先看一段有关"装饰"的词语解释&#xf…

设计模式第20讲——备忘录模式(Memento)

目录 一、什么是备忘录模式 二、角色组成 三、优缺点 四、应用场景 五、代码实现 5.0 UML类图 5.1 EditorMemento——备忘录&#xff08;Memento&#xff09; 5.2 Editor——源发器&#xff08;Originator&#xff09; 5.3 History——管理者&#xff08;Caretaker&a…

java常见算法篇【完整版】

14-常见算法 基本查找/顺序查找 从0索引依次向后查找 二分查找/折半查找 前提条件&#xff1a;数组中的数据必须是有序的核心逻辑&#xff1a;每次排除一半的查找范围 package io.xiaoduo.arithmetic;public class Test1 {public static void main(String[] args) {int[…

2023年10个最佳商业WordPress主题(经过测试和专家挑选)

正在寻找最好的商业WordPress主题&#xff1f; 为了帮助您找到适合您业务的完美主题&#xff0c;我们浏览并测试了15个最受欢迎的商业WordPress主题……然后将列表缩减为10个&#xff0c;为您提供最好的。 您需要制作一个成功的商业网站&#xff0c;以激发对潜在客户的信任并…

哪个平板电脑触控笔比较好用?便宜好用的触控笔测评

对于现在的iPad用户来说&#xff0c;苹果原装的Pencil绝对是最好的电容笔选择。但因为价格太高&#xff0c;很多人都买不起。所以&#xff0c;在实际应用中&#xff0c;如何选择一个具有高性能高性价比的电容笔就显得尤为重要。本人是一名“苹果粉”&#xff0c;又是一个老资格…

力扣方法总结-----动态规划

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、动态规划二、动态规划五部曲1.确定DP数组的含义2.确定递推公式3.确定初始值4.确定遍历顺序5.举例推导DP数组&#xff0c;打印程序中DP数组日志 三、例子 一、动…

Day 58-59 Naive Bayes算法

代码&#xff1a; package dl;import java.io.FileReader; import java.util.Arrays; import java.util.Random;import weka.core.*;/*** The Naive Bayes algorithm.*/public class NaiveBayes {/*** An inner class to store parameters.*/private class GaussianParameters…

el-tree 展开指定层级 节点

示例&#xff1a;只展开一级节点 代码实现&#xff1a; element UI 文档 html代码 defaultExpandedArr 是重点 需要加node-key <el-tree:props"defaultProps":data"treeData":default-expanded-keys"defaultExpandedArr"node-key"id&q…

第十六章Java多线程常见模式

文章目录 同步模式之保护性暂停带超时版 GuardedObjectjoin 原理多任务版 GuardedObject 异步模式之生产者/消费者模式标准库中的阻塞队列阻塞队列的实现加锁实现 生产者消费者模型的作用是什么 同步模式之保护性暂停 定义 即 Guarded Suspension&#xff0c;用在一个线程等待…

LeetCode_链表_中等_445.两数相加 II

目录 1.题目2.思路3.代码实现&#xff08;Java&#xff09; 1.题目 给你两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数字都不会以零开头。 …

C#异常总结

C#异常总结 定义Try语句异常类创建用户自定义异常搜索调用栈的示例异常抛出 定义 程序中的运行时错误&#xff0c;它违反一个系统约束或应用程序约束&#xff0c;或出现了在正常操作时未预料的情形。 Try语句 指明被异常保护的代码块&#xff0c;并提供代码以处理异常。try由…

ICC2:fixed和locked有什么不同?

如题&#xff0c;physical status中locked与fixed有很多小伙伴会搞混&#xff0c;从实用性的角度来讲这两个并没有什么区别&#xff0c;一是工具都不会更改object这两种属性&#xff0c;二是工具都不会在优化过程中移除这两个属性的object。 所以&#xff0c;唯一的区别在于lo…