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();
}

启动过程分为两部分

  1. main0(args):启动 NamesrvController
  2. controllerManagerMain():启动 ControllerManager

本文只分析 NamesrvController 的启动

namesrv.NamesrvStartup#main0 方法,代码如下:

public static NamesrvController main0(String[] args) {
    try {
        // 解析命令行参数和配置文件
        parseCommandlineAndConfigFile(args);
        // 创建并启动 NamesrvController
        NamesrvController controller = createAndStartNamesrvController();
        return controller;
    } catch (Throwable e) {
        e.printStackTrace();
        System.exit(-1);
    }

    return null;
}

namesrv的启动过程可以分为以下几个步骤:

  1. 解析命令行参数和配置文件,初始化namesrvConfig、nettyServerConfig、nettyClientConfig等配置对象
  2. 创建并启动 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);

}

上述代码的执行流程如下:

  1. 创建一个NamesrvConfig对象,用于存储namesrv的配置信息,如rocketmqHome、kvConfigPath等。NamesrvConfig类的具体属性如下表所示

    属性名含义默认值
    rocketmqHomeRocketMQ的根目录环境变量ROCKETMQ_HOME
    kvConfigPathKV配置文件的路径${user.home}/namesrv/kvConfig.json
    configStorePath配置存储文件的路径${user.home}/namesrv/namesrv.properties
    productEnvName环境名称center
    clusterTest是否开启集群测试false
    orderMessageEnable是否支持顺序消息false
  2. 创建一个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
  3. 创建一个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
  4. 解析命令行参数,支持以下选项:

    • -c 指定配置文件路径,如果指定了配置文件,会从文件中加载namesrvConfig、nettyServerConfig和nettyClientConfig的属性。
    • -p 打印所有配置信息,并退出程序
    • 其他选项会被转换为properties对象,并覆盖namesrvConfig、nettyServerConfig和nettyClientConfig的属性

2 创建并启动 NamesrvController

创建并启动 NamesrvController 的方法是 namesrv.NamesrvStartup#createAndStartNamesrvController

public static NamesrvController createAndStartNamesrvController() throws Exception {
    // 创建 NamesrvController
    NamesrvController controller = createNamesrvController();
    // 启动 NamesrvController
    start(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;
}

代码执行流程如下:

  1. 调用createNamesrvController方法,创建NamesrvController对象
  2. 调用start方法,传入NamesrvController对象,启动 NamesrvController 对象
  3. 格式化一个提示信息,包含序列化类型、绑定地址和监听端口信息
  4. 使用log对象记录提示信息到日志文件中
  5. 把提示信息打印到控制台中
  6. 返回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 discard
    controller.getConfiguration().registerConfig(properties);
    return controller;
}

创建 NamesrvController 对象的步骤如下:

  1. 创建一个NamesrvController对象,传入namesrvConfig、nettyServerConfig和nettyClientConfig对象,这些对象存储了namesrv的业务配置和网络配置
  2. 调用NamesrvController对象的getConfiguration方法,获取一个Configuration对象,该对象负责管理namesrv的配置信息
  3. 调用Configuration对象的registerConfig方法,传入properties对象,将properties对象中的配置信息注册到Configuration对象中
  4. 返回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");
    }

    // 初始化controller
    boolean 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;
    }));

    // 启动controller
    controller.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和remotingServer

    remotingClient是一个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_")) {
            @Override
            protected <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_")) {
            @Override
            protected <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 temporarily
            ClientRequestProcessor 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 将最重要的客户端路由请求单独隔离出来,使用不同的线程池和队列,队列大小和线程数都可以配置,这样可以保证不同类型的请求之间不会相互影响,如下图所示

    img
  • 调用 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 config
    if (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 的启动流程结束

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

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

相关文章

爬虫学习(网页解析)

目录 了解&#xff1a; 参考图 介绍 bs4库&#xff1a; 解析器&#xff1a; 解析方法 代码示例 lxml库&#xff1a; 解析器 解析方法 代码示例 了解&#xff1a; 参考图 (1) html解析器&#xff1a; (2) 解析方式&#xff1a; 介绍 ### 前言&#xff1a; 网页…

财政分权数据集:省级地级市财政分权度(1999-2021年)

财政分权是指中央政府和地方政府在财政收入和支出方面各自拥有一定的自主权&#xff0c;即政府财政权力在中央和地方之间进行分割和分配的一种制度安排。财政分权的实施可以促进地方政府的责任感和创造力&#xff0c;提高政府的效率和服务水平&#xff0c;同时也可以增强地方政…

蓝桥杯嵌入式第十三届(第二套客观题)

文章目录 前言一、题目1二、题目2三、题目3四、题目4五、题目5六、题目6七、题目7八、题目8九、题目9十、题目10总结前言 本篇文章继续讲解客观题。 一、题目1 这个其实属于送分题,了解嵌入式或者以后想要入行嵌入式的同学应该都对嵌入式特点有所了解。 A. 采用专用微控制…

KDZD-JP简易式机械碰撞试验台

一、产品制作标准 依据高压开关标准要求制作&#xff1a;GB7251-2013《低压成套开关设备和控制设备》、GB3906-2006 《3.6KV-40.5KV 交流金属封闭开关设备和控制设备》、GB/T 11022—2011《高压开关设备和控制设备标准的共用技术要求》、GB/T 1984—2014 《高压交流断路器》、…

全网最细的自定义类型详解(结构体,枚举,联合),友友们快来接收吧

各位csdn的友友们肯定都掌握了c语言中char&#xff0c;short, int, long, float, double的类型,这些都是我们c语言中的一些内置类型&#xff0c;其实c语言是可以允许我们创造一些类型的&#xff0c;今天阿博就带领友友们一起掌握这些新的自定义类型&#x1f60a;&#x1f60a;&…

[Java Web]Session | 一文详细介绍会话跟踪技术中的Session

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;Java Web 目录Session1、介绍2、工作流程3、工作原理4、基本使用5、Session的钝化与活化5.1、提出问题5.2、&…

【C语言蓝桥杯每日一题】—— 递增序列

【C语言蓝桥杯每日一题】—— 递增序列&#x1f60e;前言&#x1f64c;递增序列&#x1f64c;总结撒花&#x1f49e;&#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&#xff1a;全神贯注的上吧&#xff01;&#xff01;&#xff01; &#x1f60a;作者…

软考中级-软件工程

1 软件过程1.1 能力成熟度模型&#xff08;CMM&#xff09;初始&#xff08;混乱&#xff09;->可重复&#xff08;建立基本、重复以往&#xff09;->已定义&#xff08;文档化、标准化&#xff09;->已管理&#xff08;制定产品质量标准&#xff09;->优化&#x…

HTML5 SSE

HTML5 服务器发送事件(Server-Sent Events) 服务器发送事件&#xff08;Server-sent Events&#xff09;是基于 WebSocket 协议的一种服务器向客户端发送事件和数据的单向通讯。 HTML5 服务器发送事件&#xff08;server-sent event&#xff09;允许网页获得来自服务器的更新。…

快速将PDF转换为图片:免费的在线PDF转换器

在现代数字时代&#xff0c;PDF是一种非常常见的文件格式。它们在学术界&#xff0c;商业领域和许多其他领域中被广泛使用。有时&#xff0c;您可能需要将PDF文件转换为图像格式&#xff0c;以便能够方便地与他人共享和使用。在这种情况下&#xff0c;您可以使用免费的在线PDF转…

PyCharm 配置sqlite3驱动

在PyCharm中可以查看sqlite3数据库&#xff0c;具体要如何做呢&#xff1f; 数据库入口 打开PyCharm&#xff0c; 在最右侧&#xff0c;有一个Database的表示&#xff0c;点击如下图所示。 如果没有找到这个选项&#xff0c; 点击View -> Tool Windows -> Database同…

chatgpt实际是怎样工作的?

文章翻译自&#xff1a; https://www.assemblyai.com/blog/how-chatgpt-actually-works/ ChatGPT 是 OpenAI 的最新语言模型&#xff0c;比其前身 GPT-3 有了重大改进。与许多大型语言模型类似&#xff0c;ChatGPT 能够为不同目的生成多种样式的文本&#xff0c;但具有更高的精…

MBD-有感(Hall)开环BLDC控制模型(下)

目录 前面 保护策略 DC_Bus_Measurements Protection_Check 外设配置 最后 前面 上一篇已经把霍尔有感BLDC开环控制模型的主要部分分析完成了 MBD-有感(Hall)开环BLDC控制模型&#xff08;上&#xff09; 语雀 这一篇分析一些边边角角&#xff0c;但不成体系的部分。…

全网最详细,Jmeter接口测试场景-万条测试数据校验结果,循环断言(案例)

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 在工作中&#xff0…

这个ChatGPT插件可以远程运行代码,还生成图表

ChatGPT的插件使数据科学成为一种简单、愉快的体验 我们做数据分析时一般都是使用这样的流程来进行&#xff1a;运行jupyter notebook、安装库、解决依赖关系和版本控制&#xff0c;数据分析&#xff0c;生成图表。ChatGPT的“Code Interpreter”插件可以帮助我们进行数据分析…

【Redis】五大数据结构及其常用指令

文章目录说明String类型List类型Set类型Hash类型Sorted Set 排序集合总结说明 Redis里面的数据类型有String、List、Set、Hash、Zset。这篇文章会介绍这5种数据类型并介绍操作它们的指令. String类型 字符串是一种最基本的Redis值类型。Redis字符串是二进制安全的&#xff0…

5.1.1 Ext JS之Grid actioncolumn动作列的动态显示

在Ext JS 的 Grid中添加动作列的方式是配置一个 actioncolumn类型的动作列,这个动作列可以包含多个图表的按钮。 添加的方式如下: {xtype: actioncolumn,items:[{iconCls: x-fa fa-trash,}]}在有的时候场景中, 会根据不同行的数据来决定是否显示动作按钮, 也就是最后的效果…

关于OpenAI的DALL的一点使用心得

文章目录注册DALL使用根据描述来generate上传图片来generate也可以根据描述信息或者相似的图片来进行设计注册DALL https://openai.com/product/dall-e-2 使用 根据描述来generate surprise me 自动生成描述&#xff08;因为每个月只有15个免费credits&#xff0c;节省起见…

java面试题(持续更新)

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

Microsoft Dynamics 365 Business Central Planning Worksheet中Action Message状态变化

学习目标&#xff1a; 掌握Planning Worksheet中Action Message状态变化 学习内容&#xff1a; 掌握 创建物料&#xff0c;工作中心&#xff0c;工艺路线&#xff0c;BOM&#xff0c;物料和工艺路线&#xff0c;BOM的关联掌握 按订单的生产的物料卡片设置掌握 创建销售订单并…
最新文章