一、Nacos Config 事件监听

在实际项目中一般都会使用线程池解决一些异步并发问题,不过线程池核心参数很大程度上一次性进行设置,但系统运行起来总有可能出现各种各样的问题,如果修改线程池的参数则一般需要重启项目,对于生产环境而言有时无法进行重启,这种情况下就需要我们做成可动态调节的线程池。以应对突发情况。

在微服务项目中,目前使用 Nacos 作为配置中心已经是非常普遍的现象,得益于 Nacos 的灵活性以及稳定性,本文我们基于 Nacos Config 配置能力,将线程池的核心参数配置到 Nacos 中,并通过Nacos Config 的事件监听机制,触发动态参数调节。

实验开始前,需要安装好 Nacos 服务:

怎么监控nacos是否正常 nacos事件监听_怎么监控nacos是否正常

线程池这里使用 springframework 提供的 ThreadPoolTaskExecutor ,并通过其自带的方法进行动态调节:

怎么监控nacos是否正常 nacos事件监听_怎么监控nacos是否正常_02

二、准备测试项目并创建配置文件

首先创建一个 SpringBoot 项目,并引入 nacos config 的依赖,这里为了方便操作属性 ,同时引入 lombokSpring Cloud Alibaba 版本使用的 2.2.6.RELEASE

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.2.6.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

创建 bootstrap.yml 文件增加 nacos config 的配置,注意这里 application.namedemo-service ,下面创建配置文件命名时需要注意。

spring:
  application:
    name: demo-service
  profiles:
    active: thread # 引入thread配置
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.244.1:8848
      config:
        server-addr: 192.168.244.1:8848
        file-extension: yaml

这里单独引入一个 thread 配置,关于线程的配置都放在这里,文件名应该为 demo-service-thread.yaml

Nacos 创建 demo-service.yml 主配置文件,将服务的端口放在这里 :

server:
  port: 8082

怎么监控nacos是否正常 nacos事件监听_spring boot_03

接着创建 demo-service-thread.yaml 文件,声明出核心线程数、最大线程数、线程保持时间、以及队列的存储个数

thread:
  corePoolSize: 25
  maxPoolSize: 30
  keepAliveSeconds: 4
  queueCapacity: 200

怎么监控nacos是否正常 nacos事件监听_线程池_04


怎么监控nacos是否正常 nacos事件监听_java_05

二、声明线程池

首先对上面的配置文件,声明一个 Properties 接收参数:

@Data
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "thread")
public class ThreadPoolProperties {

    /**
     * 核心线程数
     */
    private Integer corePoolSize;
    /**
     * 最大线程数
     */
    private Integer maxPoolSize;
    /**
     * 线程保持时间 秒
     */
    private Integer keepAliveSeconds;

    /**
     * 队列的存储个数
     */
    private Integer queueCapacity;

}

使用参数声明线程池,其中提供出调整线程池参数,和打印当前参数方法:

@Data
@Configuration
public class ThreadPoolConfig {

    @Resource(name = "dynamic_thread_pool")
    ThreadPoolTaskExecutor executor;

    @Bean(value = "dynamic_thread_pool")
    public ThreadPoolTaskExecutor dynamicThreadPool(ThreadPoolProperties properties) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(properties.getCorePoolSize());
        executor.setMaxPoolSize(properties.getMaxPoolSize());
        executor.setKeepAliveSeconds(properties.getKeepAliveSeconds());
        executor.setQueueCapacity(properties.getQueueCapacity());
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }

    /**
     * 重新设置参数
     */
    public void restThreadPool(ThreadPoolProperties properties) {
        executor.setCorePoolSize(properties.getCorePoolSize());
        executor.setMaxPoolSize(properties.getMaxPoolSize());
        executor.setKeepAliveSeconds(properties.getKeepAliveSeconds());
        executor.setQueueCapacity(properties.getQueueCapacity());
    }

    /**
     * 打印线程池参数
     */
    public void printParam() {
        System.out.println("当前核心线程数:" + executor.getCorePoolSize());
        System.out.println("当前最大线程数:" + executor.getMaxPoolSize());
        System.out.println("当前线程保持时间:" + executor.getKeepAliveSeconds());
    }

}

三、添加 demo-service-thread.yaml 文件的监听

添加监听,这里我用的 PropertiesListener ,可以将修改后的配置,映射为 Properties 对象,如果想要拿到修改后配置内容的字符串可以使用 AbstractListenerListener

@Component
public class NacosListener implements ApplicationRunner {

    @Resource
    NacosConfigManager nacosConfigManager;

    @Resource
    NacosConfigProperties nacosConfigProperties;

    @Resource
    ThreadPoolConfig threadPoolConfig;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("初始线程状态:");
        threadPoolConfig.printParam();
        //配置修改监听
        nacosConfigManager.getConfigService().addListener("demo-service-thread.yaml", nacosConfigProperties.getGroup(),
                new PropertiesListener(){
                    @Override
                    public void innerReceive(Properties properties) {
                        System.out.println("配置被修改,重新调整线程池!");
                        ThreadPoolProperties poolProperties = new ThreadPoolProperties();
                        poolProperties.setCorePoolSize(Integer.parseInt(properties.getProperty("corePoolSize")));
                        poolProperties.setMaxPoolSize(Integer.parseInt(properties.getProperty("maxPoolSize")));
                        poolProperties.setKeepAliveSeconds(Integer.parseInt(properties.getProperty("keepAliveSeconds")));
                        poolProperties.setQueueCapacity(Integer.parseInt(properties.getProperty("queueCapacity")));
                        System.out.println("开始重新调整线程池...");
                        threadPoolConfig.restThreadPool(poolProperties);
                        System.out.println("调整之后线程池的参数:");
                        threadPoolConfig.printParam();
                    }
                });
    }
}

这里需要注意下,我在触发监听后,手动获取的参数,上面细心地会发现,其实ThreadPoolProperties 已经加了 @RefreshScope 会自动刷新的,那这里为啥还要手动解析呢,答案就是配置更新后,会首先触发这里的监听,如果在监听中直接调用调整方法,使用 ThreadPoolProperties 的话有可能还是原来的参数,保险起见这里获取一下。

四、测试

启动项目,可以看到初始的参数:

怎么监控nacos是否正常 nacos事件监听_spring boot_06

下面修改最大线程数为 50

怎么监控nacos是否正常 nacos事件监听_spring cloud_07

发布后,观察日志:

怎么监控nacos是否正常 nacos事件监听_java_08

线程池的最大线程已经被调整为 50