RocketMQ 5.1 NameServer 启动流程
文章目录
- 1 解析命令行参数和配置文件
- 2 创建并启动 NamesrvController
- 2.1 创建 NamesrvController 对象
- 2.2 启动 NamesrvController 对象
- 第一步:初始化 controller
- 第二步:注册 JVM 钩子
- 第二步:启动 controller
RocketMQ是一个分布式消息中间件,它的核心组件之一是namesrv,负责管理broker的路由信息和kv配置。本文将介绍RocketMQ5.1版本中namesrv的启动过程,包括如何解析命令行参数、加载配置文件、初始化和启动namesrv控制器等。
首先,我们需要在环境变量中设置ROCKETMQ_HOME,指向RocketMQ的安装目录。然后,我们可以使用如下命令启动namesrv:
nohup sh mqnamesrv &
这条命令执行运行的是 namesrv.NamesrvStartup#main
方法。
public static void main(String[] args) {main0(args);controllerManagerMain();
}
启动过程分为两部分
main0(args)
:启动 NamesrvControllercontrollerManagerMain()
:启动 ControllerManager
本文只分析 NamesrvController 的启动
即 namesrv.NamesrvStartup#main0
方法,代码如下:
public static NamesrvController main0(String[] args) {try {// 解析命令行参数和配置文件parseCommandlineAndConfigFile(args);// 创建并启动 NamesrvControllerNamesrvController controller = createAndStartNamesrvController();return controller;} catch (Throwable e) {e.printStackTrace();System.exit(-1);}return null;
}
namesrv的启动过程可以分为以下几个步骤:
- 解析命令行参数和配置文件,初始化namesrvConfig、nettyServerConfig、nettyClientConfig等配置对象
- 创建并启动 NamesrvController 对象,该对象是namesrv的核心控制器,它持有各种配置对象、网络通信对象、路由管理对象等
1 解析命令行参数和配置文件
解析命令行参数和配置文件的方法是 namesrv.NamesrvStartup#parseCommandlineAndConfigFile
,代码如下:
public static void parseCommandlineAndConfigFile(String[] args) throws Exception {System.setProperty(RemotingCommand.REMOTING_VERSION_KEY, Integer.toString(MQVersion.CURRENT_VERSION));Options options = ServerUtil.buildCommandlineOptions(new Options());CommandLine commandLine = ServerUtil.parseCmdLine("mqnamesrv", args, buildCommandlineOptions(options), new DefaultParser());if (null == commandLine) {System.exit(-1);return;}// 创建配置类对象namesrvConfig = new NamesrvConfig();nettyServerConfig = new NettyServerConfig();nettyClientConfig = new NettyClientConfig();// 设置namesrv的监听端口nettyServerConfig.setListenPort(9876);// 如果命令行中有-c参数,则从配置文件中加载namesrvConfig、nettyServerConfig、nettyClientConfig、controllerConfig的属性if (commandLine.hasOption('c')) {String file = commandLine.getOptionValue('c');if (file != null) {InputStream in = new BufferedInputStream(Files.newInputStream(Paths.get(file)));properties = new Properties();properties.load(in);MixAll.properties2Object(properties, namesrvConfig);MixAll.properties2Object(properties, nettyServerConfig);MixAll.properties2Object(properties, nettyClientConfig);if (namesrvConfig.isEnableControllerInNamesrv()) {controllerConfig = new ControllerConfig();MixAll.properties2Object(properties, controllerConfig);}namesrvConfig.setConfigStorePath(file);System.out.printf("load config properties file OK, %s%n", file);in.close();}}MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), namesrvConfig);// 如果命令行中有-p参数,则打印namesrvConfig、nettyServerConfig、nettyClientConfig、controllerConfig的属性if (commandLine.hasOption('p')) {MixAll.printObjectProperties(logConsole, namesrvConfig);MixAll.printObjectProperties(logConsole, nettyServerConfig);MixAll.printObjectProperties(logConsole, nettyClientConfig);if (namesrvConfig.isEnableControllerInNamesrv()) {MixAll.printObjectProperties(logConsole, controllerConfig);}System.exit(0);}if (null == namesrvConfig.getRocketmqHome()) {System.out.printf("Please set the %s variable in your environment to match the location of the RocketMQ installation%n", MixAll.ROCKETMQ_HOME_ENV);System.exit(-2);}MixAll.printObjectProperties(log, namesrvConfig);MixAll.printObjectProperties(log, nettyServerConfig);}
上述代码的执行流程如下:
-
创建一个NamesrvConfig对象,用于存储namesrv的配置信息,如rocketmqHome、kvConfigPath等。NamesrvConfig类的具体属性如下表所示
属性名 含义 默认值 rocketmqHome RocketMQ的根目录 环境变量ROCKETMQ_HOME kvConfigPath KV配置文件的路径 ${user.home}/namesrv/kvConfig.json configStorePath 配置存储文件的路径 ${user.home}/namesrv/namesrv.properties productEnvName 环境名称 center clusterTest 是否开启集群测试 false orderMessageEnable 是否支持顺序消息 false -
创建一个NettyServerConfig对象,用于存储netty服务端的配置信息,如listenPort、serverWorkerThreads等。NettyServerConfig类的具体属性如下表所示
属性名 含义 默认值 listenPort 监听端口,用于接收客户端的连接请求 9876 serverWorkerThreads 服务端工作线程数量,用于处理网络IO事件或执行业务任务 8 serverCallbackExecutorThreads 服务端回调执行线程数量,用于执行异步回调任务,如果为0,则使用公共线程池 0 serverSelectorThreads 服务端选择器线程数量,用于接收和分发网络IO事件,建议不要超过3个 3 serverOnewaySemaphoreValue 服务端单向请求信号量值,用于限制单向请求的并发度,防止资源耗尽 256 serverAsyncSemaphoreValue 服务端异步请求信号量值,用于限制异步请求的并发度,防止资源耗尽 64 serverChannelMaxIdleTimeSeconds 服务端通道最大空闲时间(秒),超过该时间则关闭连接,释放资源 120 serverSocketSndBufSize 服务端Socket发送缓冲区大小(字节),如果为-1,则使用操作系统默认值 -1 serverSocketRcvBufSize 服务端Socket接收缓冲区大小(字节),如果为-1,则使用操作系统默认值 -1 serverPooledByteBufAllocatorEnable 是否启用服务端池化ByteBuf分配器,可以提高内存利用率和性能 true useEpollNativeSelector 是否使用Epoll本地选择器,可以提高Linux平台下的网络IO效率,需要操作系统支持 false -
创建一个NettyClientConfig对象,用于存储netty客户端的配置信息,如clientWorkerThreads、clientCallbackExecutorThreads等。NettyClientConfig类的具体属性如下表所示
属性名 含义 默认值 clientWorkerThreads 客户端工作线程数量,用于处理网络IO事件或执行业务任务 4 clientCallbackExecutorThreads 客户端回调执行线程数量,用于执行异步回调任务,如果为0,则使用公共线程池 可用处理器的数量 connectTimeoutMillis 连接超时时间(毫秒),超过该时间则放弃连接 3000 channelNotActiveInterval 通道不活跃的间隔时间(毫秒),超过该时间则关闭通道,释放资源 1000 * 60 clientChannelMaxIdleTimeSeconds 客户端通道最大空闲时间(秒),超过该时间则关闭连接,释放资源 120 clientSocketSndBufSize 客户端Socket发送缓冲区大小(字节),如果为-1,则使用操作系统默认值 -1 clientSocketRcvBufSize 客户端Socket接收缓冲区大小(字节),如果为-1,则使用操作系统默认值 -1 clientPooledByteBufAllocatorEnable 是否启用客户端池化ByteBuf分配器,可以提高内存利用率和性能 false clientCloseSocketIfTimeout 是否在超时时关闭客户端Socket,可以避免资源泄漏 false useTLS 是否使用TLS协议进行加密通信,需要操作系统支持 false clientOnewaySemaphoreValue 客户端单向请求信号量值,用于限制单向请求的并发度,防止资源耗尽 65535 clientAsyncSemaphoreValue 客户端异步请求信号量值,用于限制异步请求的并发度,防止资源耗尽 65535 -
解析命令行参数,支持以下选项:
-c
指定配置文件路径,如果指定了配置文件,会从文件中加载namesrvConfig、nettyServerConfig和nettyClientConfig的属性。-p
打印所有配置信息,并退出程序- 其他选项会被转换为properties对象,并覆盖namesrvConfig、nettyServerConfig和nettyClientConfig的属性
2 创建并启动 NamesrvController
创建并启动 NamesrvController 的方法是 namesrv.NamesrvStartup#createAndStartNamesrvController
:
public static NamesrvController createAndStartNamesrvController() throws Exception {// 创建 NamesrvControllerNamesrvController controller = createNamesrvController();// 启动 NamesrvControllerstart(controller);// 输出启动成功的日志NettyServerConfig serverConfig = controller.getNettyServerConfig();String tip = String.format("The Name Server boot success. serializeType=%s, address %s:%d", RemotingCommand.getSerializeTypeConfigInThisServer(), serverConfig.getBindAddress(), serverConfig.getListenPort());log.info(tip);System.out.printf("%s%n", tip);return controller;
}
代码执行流程如下:
- 调用createNamesrvController方法,创建NamesrvController对象
- 调用start方法,传入NamesrvController对象,启动 NamesrvController 对象
- 格式化一个提示信息,包含序列化类型、绑定地址和监听端口信息
- 使用log对象记录提示信息到日志文件中
- 把提示信息打印到控制台中
- 返回NamesrvController对象
如果在执行中出现异常,则抛出异常并退出程序
2.1 创建 NamesrvController 对象
在 namesrv.NamesrvStartup#createAndStartNamesrvController
方法中调用了 namesrv.NamesrvStartup#createNamesrvController
方法,用于创建 NamesrvController 对象,代码为:
public static NamesrvController createNamesrvController() {// 创建 NamesrvController,传入namesrvConfig、nettyServerConfig、nettyClientConfig配置final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig, nettyClientConfig);// remember all configs to prevent discardcontroller.getConfiguration().registerConfig(properties);return controller;
}
创建 NamesrvController 对象的步骤如下:
- 创建一个NamesrvController对象,传入namesrvConfig、nettyServerConfig和nettyClientConfig对象,这些对象存储了namesrv的业务配置和网络配置
- 调用NamesrvController对象的getConfiguration方法,获取一个Configuration对象,该对象负责管理namesrv的配置信息
- 调用Configuration对象的registerConfig方法,传入properties对象,将properties对象中的配置信息注册到Configuration对象中
- 返回NamesrvController对象
这个函数的返回值是NamesrvController对象
2.2 启动 NamesrvController 对象
在 namesrv.NamesrvStartup#createAndStartNamesrvController
方法中调用了 namesrv.NamesrvStartup#start
方法,用于启动创建好的 NamesrvController 对象,代码为:
public static NamesrvController start(final NamesrvController controller) throws Exception {if (null == controller) {throw new IllegalArgumentException("NamesrvController is null");}// 初始化controllerboolean initResult = controller.initialize();if (!initResult) {controller.shutdown();System.exit(-3);}// 当jvm关闭的时候,会执行系统中已经设置的所有通过方法addShutdownHook添加的钩子,当系统执行完这些钩子后,jvm才会关闭。// 所以这些钩子可以在jvm关闭的时候进行内存清理、对象销毁等操作。Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, (Callable<Void>) () -> {controller.shutdown();return null;}));// 启动controllercontroller.start();return controller;
}
启动 NamesrvController 对象的步骤如下:
第一步:初始化 controller
调用 namesrv.NamesrvController#initialize
方法,初始化controller。如果初始化失败,就调用controller的shutdown方法,关闭controller,并退出程序
public boolean initialize() {loadConfig();initiateNetworkComponents();initiateThreadExecutors();registerProcessor();startScheduleService();initiateSslContext();initiateRpcHooks();return true;
}
初始化操作包括:
-
调用
namesrv.NamesrvController#loadConfig
方法从 NamesrvConfig 对象中保存的 kvConfigPath 指定的文件中加载kv配置,并创建一个KVConfigManager对象,用于管理和打印kv配置private void loadConfig() {this.kvConfigManager.load(); }
-
调用
namesrv.NamesrvController#initiateNetworkComponents
方法初始化网络组件,包括remotingClient和remotingServerremotingClient是一个NettyRemotingClient对象,它用于向其他服务发送请求或响应。remotingServer是一个NettyRemotingServer对象,它用于接收和处理来自其他服务的请求或响应。BrokerHousekeepingService对象用于处理broker的连接和断开事件。
private void initiateNetworkComponents() {this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);this.remotingClient = new NettyRemotingClient(this.nettyClientConfig); }
-
调用
namesrv.NamesrvController#initiateThreadExecutors
方法初始化两个线程池,一个是defaultExecutor,用于处理默认的远程请求;另一个是clientRequestExecutor,用于处理客户端的路由信息请求。这两个线程池都使用了LinkedBlockingQueue作为任务队列,并且重写了newTaskFor方法,使用FutureTaskExt包装了Runnable任务。private void initiateThreadExecutors() {this.defaultThreadPoolQueue = new LinkedBlockingQueue<>(this.namesrvConfig.getDefaultThreadPoolQueueCapacity());this.defaultExecutor = new ThreadPoolExecutor(this.namesrvConfig.getDefaultThreadPoolNums(),this.namesrvConfig.getDefaultThreadPoolNums(), 1000 * 60, TimeUnit.MILLISECONDS,this.defaultThreadPoolQueue, new ThreadFactoryImpl("RemotingExecutorThread_")) {@Overrideprotected <T> RunnableFuture<T> newTaskFor(final Runnable runnable, final T value) {return new FutureTaskExt<>(runnable, value);}};this.clientRequestThreadPoolQueue = new LinkedBlockingQueue<>(this.namesrvConfig.getClientRequestThreadPoolQueueCapacity());this.clientRequestExecutor = new ThreadPoolExecutor(this.namesrvConfig.getClientRequestThreadPoolNums(),this.namesrvConfig.getClientRequestThreadPoolNums(), 1000 * 60, TimeUnit.MILLISECONDS,this.clientRequestThreadPoolQueue, new ThreadFactoryImpl("ClientRequestExecutorThread_")) {@Overrideprotected <T> RunnableFuture<T> newTaskFor(final Runnable runnable, final T value) {return new FutureTaskExt<>(runnable, value);}}; }
-
调用
namesrv.NamesrvController#registerProcessor
方法根据namesrvConfig.isClusterTest()
的值,选择使用ClusterTestRequestProcessor或者DefaultRequestProcessor作为默认处理器- ClusterTestRequestProcessor是一个用于集群测试的处理器,它会在请求前后添加一些环境信息,比如产品环境名称、请求时间等
- DefaultRequestProcessor是一个用于正常运行的处理器,它会根据请求的类型,调用不同的方法来处理,比如注册Broker、获取路由信息、更新配置等。
- 在
namesrvConfig.isClusterTest() = false
时如果收到请求的requestCode
等于RequestCode.GET_ROUTEINFO_BY_TOPIC
则会使用ClientRequestProcessor来处理;当收到其他请求时,会使用DefaultRequestProcessor来处理。
private void registerProcessor() {if (namesrvConfig.isClusterTest()) {this.remotingServer.registerDefaultProcessor(new ClusterTestRequestProcessor(this, namesrvConfig.getProductEnvName()), this.defaultExecutor);} else {// Support get route info only temporarilyClientRequestProcessor clientRequestProcessor = new ClientRequestProcessor(this);this.remotingServer.registerProcessor(RequestCode.GET_ROUTEINFO_BY_TOPIC, clientRequestProcessor, this.clientRequestExecutor);this.remotingServer.registerDefaultProcessor(new DefaultRequestProcessor(this), this.defaultExecutor);} }
此部分为 RIP 29 Optimize RocketMQ NameServer 中
Thread pool separation
改进。在改进之前nameserver使用同一个线程池和队列来处理所有的客户端路由请求、broker注册请求等,如果其中一种类型的请求爆发,会影响所有的请求。为了解决这个问题,RIP-29 将最重要的客户端路由请求单独隔离出来,使用不同的线程池和队列,队列大小和线程数都可以配置,这样可以保证不同类型的请求之间不会相互影响,如下图所示 -
调用
namesrv.NamesrvController#startScheduleService
方法启动定时服务,执行以下三个任务:- 每隔一段时间扫描不活跃的broker,并清理路由信息
- 每隔 10 分钟打印所有的KV配置信息
- 每隔 1 秒打印线程池的水位日志,即客户端请求线程池和默认线程池的队列大小和头部任务的慢时间(从创建到执行的时间)
private void startScheduleService() {// 周期性扫描不活跃的Broker,并从路由信息中移除this.scanExecutorService.scheduleAtFixedRate(NamesrvController.this.routeInfoManager::scanNotActiveBroker,5, this.namesrvConfig.getScanNotActiveBrokerInterval(), TimeUnit.MILLISECONDS);// 每隔 10 分钟打印KVConfig 信息this.scheduledExecutorService.scheduleAtFixedRate(NamesrvController.this.kvConfigManager::printAllPeriodically,1, 10, TimeUnit.MINUTES);// 每隔 1 秒打印线程池的水位日志,// 即客户端请求线程池和默认线程池的队列大小和头部任务的慢时间(从创建到执行的时间)this.scheduledExecutorService.scheduleAtFixedRate(() -> {try {NamesrvController.this.printWaterMark();} catch (Throwable e) {LOGGER.error("printWaterMark error.", e);}}, 10, 1, TimeUnit.SECONDS); }
-
调用
namesrv.NamesrvController#initiateSslContext
方法初始化SSL上下文,即配置remotingServer使用TLS协议进行安全通信 -
调用
namesrv.NamesrvController#initiateRpcHooks
方法注册RPC钩子,即在remotingServer处理请求之前或之后执行一些自定义的逻辑private void initiateRpcHooks() {this.remotingServer.registerRPCHook(new ZoneRouteRPCHook()); }
在
namesrv.route.ZoneRouteRPCHook
类中重写了doAfterResponse
方法,它会在处理GET_ROUTEINFO_BY_TOPIC请求(即请求的requestCode = RequestCode.GET_ROUTEINFO_BY_TOPIC
)时,根据请求中的zoneName参数,过滤掉不属于该区域的broker和queue数据,从而实现区域隔离的功能。具体来说,
doAfterResponse
会设置 response 的 body 为namesrv.route.ZoneRouteRPCHook#filterByZoneName
方法返回值的字节数组格式,filterByZoneName
方法作用是返回过滤掉不属于该区域的broker和queue数据后的TopicRouteData数据对象@Override public void doAfterResponse(String remoteAddr, RemotingCommand request, RemotingCommand response) {if (RequestCode.GET_ROUTEINFO_BY_TOPIC != request.getCode()) {return;}// 省略部分代码TopicRouteData topicRouteData = RemotingSerializable.decode(response.getBody(), TopicRouteData.class);response.setBody(filterByZoneName(topicRouteData, zoneName).encode()); }
第二步:注册 JVM 钩子
通过addShutdownHook方法注册一个ShutdownHookThread对象,即 JVM 钩子,用来在程序终止时调用controller的shutdown方法,释放资源
public void shutdown() {this.remotingClient.shutdown();this.remotingServer.shutdown();this.defaultExecutor.shutdown();this.clientRequestExecutor.shutdown();this.scheduledExecutorService.shutdown();this.scanExecutorService.shutdown();this.routeInfoManager.shutdown();if (this.fileWatchService != null) {this.fileWatchService.shutdown();}
}
第二步:启动 controller
调用 namesrv.NamesrvController#start
方法,启动 controller
public void start() throws Exception {this.remotingServer.start();// In test scenarios where it is up to OS to pick up an available port, set the listening port back to configif (0 == nettyServerConfig.getListenPort()) {nettyServerConfig.setListenPort(this.remotingServer.localListenPort());}this.remotingClient.updateNameServerAddressList(Collections.singletonList(NetworkUtil.getLocalAddress()+ ":" + nettyServerConfig.getListenPort()));this.remotingClient.start();if (this.fileWatchService != null) {this.fileWatchService.start();}this.routeInfoManager.start();
}
启动操作包括:
- 调用remotingServer对象的start方法,启动一个NettyRemotingServer,用于接收和处理客户端的请求。
- 如果nettyServerConfig对象的listenPort属性为0,说明是由操作系统自动分配一个可用端口,那么将remotingServer对象的localListenPort属性赋值给nettyServerConfig对象的listenPort属性,保持一致。
- 调用remotingClient对象的updateNameServerAddressList方法,更新本地地址列表,只包含当前机器的IP地址和端口号。
- 调用remotingClient对象的start方法,启动一个NettyRemotingClient,用于向其他服务发送请求。
- 如果fileWatchService对象不为空,调用它的start方法,启动一个文件监视服务,用于动态加载证书文件。
- 调用routeInfoManager对象的start方法,启动一个路由信息管理器,用于维护Broker和Topic的路由关系。
至此,NamesrvController 的启动流程结束
相关文章:
RocketMQ 5.1 NameServer 启动流程
文章目录1 解析命令行参数和配置文件2 创建并启动 NamesrvController2.1 创建 NamesrvController 对象2.2 启动 NamesrvController 对象第一步:初始化 controller第二步:注册 JVM 钩子第二步:启动 controllerRocketMQ是一个分布式消息中间件&…...

马云回国,首谈ChatGPT
马云今天回国了,这是一个备受关注的消息。 作为中国最具代表性的企业家之一,马云在过去的二十多年里,带领阿里巴巴从一个小小的创业公司,发展成为全球最大的电商平台之一,同时也推动了中国互联网行业的发展。 他的回…...

深入理解C++迭代器:让你的C++代码更加灵活
C迭代器:更加优雅的容器操作方式引言C迭代器简介a. 迭代器的定义b. 迭代器的作用c. 迭代器与指针的区别迭代器分类a. 输入迭代器(Input Iterator)b. 输出迭代器(Output Iterator)c. 前向迭代器(Forward Ite…...

Java 读取Excel模板中的数据到实体类
目录一. 前提条件1.1 需求1.2 分析二. 准备2.1 自定义注解2.2 封装Excel的实体类三. 前台四. Controller层五. Service层💪💪💪六. 效果一. 前提条件 1.1 需求 从指定的Excel模板中读取数据,将读取到的数据存储到数据库中。 1.2…...

【java基础】Socket网络编程
文章目录说明InetAddress介绍Socket介绍ServerSocket介绍实现简单的Socket通信总结说明 这里介绍下如何在java里面进行socket编程 InetAddress介绍 这个类表示一个Internet协议(IP)地址,我们可以通过ip或者主机名来构建这个类 Testpublic void t1() throws Except…...

转发和重定向区别
转发和重定向 1.0 面试提问 定义不同跳转的方式不同数据共享不同最终的URL地址不同代码实现不同 1. 转发 1.1 概念 转发实际上在服务器端进行页面的跳转操作,请求转发:一种在服务器内部的资源的跳转的方式。 访问A,A请求转发了B,…...

java面试题(持续更新)
java面试题(持续更新) java 基础 java面向对象有哪些特征 面向对象的三大特征:封装、继承、多态 封装:隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据,…...

【花雕学AI】09:发挥ChatGPT最大潜力——产生高质量内容的九种方法和建议
人工智能(AI)是当今科技领域最热门和最有前景的话题之一,它已经渗透到了我们生活和工作的方方面面,给我们带来了许多便利和惊喜。而在AI的众多分支中,自然语言处理(NLP)是最贴近人类的一个领域&…...

实战打靶集锦-013-Loly
**写在前面:**记录博主的一次打靶经历 目录1. 主机发现2. 端口扫描3. 服务枚举4. web服务探查4.1 WordPress探测4.2 使用metasploit4.3 使用wpscan4.4 阶段性回顾5. 提权5.1 弱密码提权5.2 操作系统信息枚举5.3 定时任务枚举5.4 passwd信息枚举5.5 可执行文件枚举5.…...

程序员OKR学习法
版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl OKR管理法 OKR(Objectives and Key Results)管理法是一种目标管理方法,旨在通过制定明确的目标和可量化的关键结果来帮助组织、团队和个人…...

【从零开始学习 UVM】6.6、UVM 激励产生 —— UVM Virtual Sequence【重要】
文章目录 使用virtual sequencer不使用virtual sequencervirtual sequence是一个容器,用于在环境中的virtual sequencer上启动多个sequence。 这个virtual sequence通常由一个具有对真实sequencers句柄的virtual sequencers执行。 需要virtual sequence的原因是当您需要在不…...
蓝桥杯:阶乘约数
蓝桥杯:阶乘约数https://www.lanqiao.cn/problems/1020/learning/ 目录 题目描述 填空题:答案是 39001250856960000 题目分析 AC代码(Java) 暴力 线性筛 题目描述 填空题 定义阶乘 n!123⋅⋅⋅n。 请问 100! (100 的阶乘)有…...

最大数字
[蓝桥杯 2022 国 B] 最大数字 题目描述 给定一个正整数 NNN。你可以对 NNN 的任意一位数字执行任意次以下 2 种操作: 将该位数字加 111。如果该位数字已经是 999,加 111 之后变成 000。 将该位数字减 111。如果该位数字已经是 000,减 111 之后变成 99…...
【java进阶08:异常】finally关键字、自定义异常类、用户业务作业、军队武器作业
java中的异常处理机制 异常在java中以类和对象的形式存在,那么异常的继承结构是怎样的?我们可以使用UML图来描述以下继承结构 画UML图的工具:Rational Rose、starUML等 Object下有Throwable(可抛出的) Throwable下有两…...

#课程笔记# 电路与电子技术基础 课堂笔记 第6章 半导体器件的基本特性
6.1 半导体基础知识 6.1.1 本征半导体 完全纯净的、结构完整的半导体称为本征半导体。 常用的半导体材料有硅和锗,它们都是四价元素,原子最外层轨道有四个价电子。 若将纯净的半导体制成晶体,则原子形成排列整齐的点阵。 点阵是由共价键提供…...

skimage.filters.apply_hysteresis_threshold详解
本文内容均参考scipy1.9.1scipy1.9.1scipy1.9.1版本的源码,若有任何不当欢迎指出 我们截取官方注释如下: def apply_hysteresis_threshold(image, low, high):"""Apply hysteresis thresholding to image.This algorithm finds regions …...

一、基础算法5:前缀和与差分 模板题+算法模板(前缀和,子矩阵的和,差分,差分矩阵)
文章目录算法模板前缀和模板子矩阵的和模板差分模板差分矩阵模板模板题前缀和原题链接题目题解子矩阵的和原题链接题目题解差分原题链接题目题解差分矩阵原题链接题目题解算法模板 前缀和模板 S[i] a[1] a[2] ... a[i] a[l] ... a[r] S[r] - S[l - 1]子矩阵的和模板 S[i…...

Python矩阵分解之QR分解
文章目录QR和RQ分解其他函数QR和RQ分解 记AAA为方阵,P,QP, QP,Q分别为正交单位阵和上三角阵,则形如AQRAQRAQR的分解为QR分解;形如ARQARQARQ的分解为RQ分解。 在scipy.linalg中,为二者提供了相同的参数,除了待分解矩阵…...

随机森林程序
n_estimators:数值型取值 含义:森林中决策树的个数,默认是10 criterion:字符型取值 含义:采用何种方法度量分裂质量,信息熵或者基尼指数,默认是基尼指数 max_features:取值为int型, float型, string类型…...

每日一练2627——变态跳台阶快到碗里来不用加减乘除做加法三角形
文章目录变态跳台阶思路:代码:快到碗里来思路:代码:不用加减乘除做加法思路:代码:三角形思路:代码:变态跳台阶 题目链接: 思路: 这个题目很容易理解&#…...

LeetCode-146. LRU 缓存
目录LRU理论题目思路代码实现一代码实现二题目来源 146. LRU 缓存 LRU理论 LRU 是 Least Recently Used 的缩写,这种算法认为最近使用的数据是热门数据,下一次很大概率将会再次被使用。而最近很少被使用的数据,很大概率下一次不再用到。当缓…...

#课程笔记# 电路与电子技术基础 课堂笔记 第3章 电路分析的几个定理
3.1 叠加定理 激励:电流源或电压源 响应:电流或电压 叠加定理一般用于已知激励或响应中的一种,求另一种。做法就是,每次只求一个激励作用下的响应,将其他激励置零,置零的具体做法是,电压源变…...

推迟参数设计的自适应反步控制和自适应神经网络的反步控制设计
推迟参数设计的自适应反步控制和自适应神经网络的反步控制设计 目录推迟参数设计的自适应反步控制和自适应神经网络的反步控制设计前言匹配与非匹配1. 基于自适应反步控制的非匹配条件下的系统控制器设计问题描述控制器设计小结2. 基于自适应反步控制和推迟参数设计的非匹配条件…...

spring5.1+SmartInstantiationAwareBeanPostProcessor 解决循环依赖
SmartInstantiationAwareBeanPostProcessor 解决循环依赖的过程, 例如上面的 A依赖B, B依赖A SmartInstantiationAwareBeanPostProcessor 是 Spring 中的一个接口,它扩展了 InstantiationAwareBeanPostProcessor 接口并提供了对 Bean 的实例化和属性填充的更高级的…...

apply、call与bind
共同点: 都是函数对象的一个方法,作用是改变函数执行时的上下文,即改变函数体内部this的指向 var name "lucy"; var obj {name: "martin",say: function () {console.log(this.name);} }; obj.say(); // martin&…...

《Effective Objective-C 2.0 》 阅读笔记 item3
第3条:多用字面量语法,少用与之等价的方法 1. 字面数值 使用字面量能令代码更为简洁: NSNumber *someNumber 1; *** 字面量语法的好处! *** 令代码更为简洁。能够以NSNumber实例表示的所有数据类型(int、float、d…...

SSL/TLS 证书管理
SSL 证书发现 随着组织的 IT 基础架构的扩展,他们为每台计算机获取证书以保护其资源和域。此外,开发人员通常会创建许多自签名证书,以便在产品的开发阶段保护内部网络。组织通常最终会拥有数千个证书。自动发现证书提供了对证书基础结构的完…...

supersqli(SQL注入流程及常用SQL语句)
目录 一、SQL注入知识学习 1、判断注入类型 (1)数字型注入判断 (2)字符型注入判断 2、猜解sql查询语句中的字段数(order by 的使用) 3、判断显示位爆数据库的名字 4、注释(--的使用&#…...

【数据结构】用Java实现一棵二叉树
目录 前言 1. 创建MyBinaryTree类 2. 从前序与中序遍历序列构造二叉树 3. 从中序与后序遍历序列构造二叉树 4. 用层序遍历验证二叉树是否构建成功 5. 整体代码(构建二叉树、二叉树的基本功能和测试代码) 6. 测试结果 前言 前面两篇文章已经给出了…...

【面试】面试官问的几率较大的网络安全面试题
文章目录防范常见的 Web 攻击1、什么是SQL注入攻击2、什么是XSS攻击3、什么是CSRF攻击4、什么是文件上传漏洞5、DDos 攻击重要协议分布图1、arp协议的工作原理ARP协议工作原理:2、什么是RARP?工作原理3、dns是什么?dns的工作原理4、rip协议是…...