SpringBoot + Nacos 实现动态化线程池

1.背景

在后台开发中,会经常用到线程池技术,对于线程池核心参数的配置很大程度上依靠经验。然而,由于系统运行过程中存在的不确定性,我们很难一劳永逸地规划一个合理的线程池参数。

在对线程池配置参数进行调整时,一般需要对服务进行重启,这样修改的成本就会偏高。一种解决办法就是,将线程池的配置放到平台侧,运行开发同学根据系统运行情况对核心参数进行动态配置。

本文以Nacos作为服务配置中心,以修改线程池核心线程数、最大线程数为例,实现一个简单的动态化线程池。

2.code

1.依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>2021.1</version>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    <version>2021.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
2.配置yml文件

bootstrap.yml

server:
  port: 8090
  # 应用名称(nacos会将该名称当做服务名称)
spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        namespace: public
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848
        file-extension: yml

application.yml

spring:
  profiles:
    active: dev

为什么要配置两个yml文件?

springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application。

nacos在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后才能保证项目的正常启动。

3.nacos配置

登录到nacos管理页面,新建配置,如下图所示

注意Data ID的命名格式为,${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension} ,在本文中,Data ID的名字就是order-service-dev.yml

配置详情

这里我们只配置了两个参数,核心线程数量和最大线程数。

4.线程池配置和nacos配置变更监听
@RefreshScope
@Configuration
public class DynamicThreadPool implements InitializingBean {
 
    @Value("${core.size}")
    private String coreSize;
 
    @Value("${max.size}")
    private String maxSize;
 
    private static ThreadPoolExecutor threadPoolExecutor;
 
    @Autowired
    private NacosConfigManager nacosConfigManager;
 
    @Autowired
    private NacosConfigProperties nacosConfigProperties;
 
    @Override
    public void afterPropertiesSet() throws Exception {
        //按照nacos配置初始化线程池
        threadPoolExecutor = new ThreadPoolExecutor(Integer.parseInt(coreSize), Integer.parseInt(maxSize), 10L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(10),
                new ThreadFactoryBuilder().setNameFormat("c_t_%d").build(),
                new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        System.out.println("rejected!");
                    }
                });
 
        //nacos配置变更监听
        nacosConfigManager.getConfigService().addListener("order-service-dev.yml", nacosConfigProperties.getGroup(),
                new Listener() {
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }
 
                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        //配置变更,修改线程池配置
                        System.out.println(configInfo);
                        changeThreadPoolConfig(Integer.parseInt(coreSize), Integer.parseInt(maxSize));
                    }
                });
    }
 
    /**
     * 打印当前线程池的状态
     */
    public String printThreadPoolStatus() {
        return String.format("core_size:%s,thread_current_size:%s;" +
                        "thread_max_size:%s;queue_current_size:%s,total_task_count:%s", threadPoolExecutor.getCorePoolSize(),
                threadPoolExecutor.getActiveCount(), threadPoolExecutor.getMaximumPoolSize(), threadPoolExecutor.getQueue().size(),
                threadPoolExecutor.getTaskCount());
    }
 
    /**
     * 给线程池增加任务
     *
     * @param count
     */
    public void dynamicThreadPoolAddTask(int count) {
        for (int i = 0; i < count; i++) {
            int finalI = i;
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(finalI);
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
 
    /**
     * 修改线程池核心参数
     *
     * @param coreSize
     * @param maxSize
     */
    private void changeThreadPoolConfig(int coreSize, int maxSize) {
        threadPoolExecutor.setCorePoolSize(coreSize);
        threadPoolExecutor.setMaximumPoolSize(maxSize);
    }
}

这个代码就是实现动态线程池和核心了,需要说明的是:

  • @RefreshScope,这个注解用来支持nacos的动态刷新功能;

  • @Value("${max.size}")@Value("${core.size}"),这两个注解用来读取我们上一步在nacos配置的具体信息;同时,nacos配置变更时,能够实时读取到变更后的内容;

  • nacosConfigManager.getConfigService().addListener,配置监听,nacos配置变更时实时修改线程池的配置。

5.controller

为了观察线程池动态变更的效果,增加Controller类。

@RestController
@RequestMapping("/threadpool")
public class ThreadPoolController {
 
    @Autowired
    private DynamicThreadPool dynamicThreadPool;
 
    /**
     * 打印当前线程池的状态
     */
    @GetMapping("/print")
    public String printThreadPoolStatus() {
        return dynamicThreadPool.printThreadPoolStatus();
    }
 
    /**
     * 给线程池增加任务
     *
     * @param count
     */
    @GetMapping("/add")
    public String dynamicThreadPoolAddTask(int count) {
        dynamicThreadPool.dynamicThreadPoolAddTask(count);
        return String.valueOf(count);
    }
}
6.测试

启动项目,访问http://localhost:8090/threadpool/print打印当前线程池的配置。

可以看到,这个就是我们之前在nacos配置的线程数。

访问http://localhost:8090/threadpool/add?count=20增加20个任务。

重新打印线程池配置

可以看到已经有线程在排队了。

为了能够看到效果,我们多访问几次/add接口,增加任务数,在控制台出现拒绝信息时调整nacos配置。

此时,执行/add命令时,所有的线程都会提示rejected。

调整nacos配置,将核心线程数调整为50,最大线程数调整为100.

重新多次访问/add接口增加任务,发现没有拒绝信息了。这时,打印具体的线程状态,发现线程池参数修改成功。

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

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

相关文章

BioTech - 交联质谱 (Crosslinks) 的常见数据格式说明

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/136190750 交联质谱 (Crosslinking Mass Spectrometry&#xff0c;Crosslinks) 技术 是一种结合化学交联剂和质谱仪的方法&#xff0c;用于研究蛋…

第十篇:node处理404和服务器错误

🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录</

Vue3+vite搭建基础架构(10)--- 使用less和vite-plugin-vue-setup-extend

Vue3vite搭建基础架构&#xff08;10&#xff09;--- 使用less和vite-plugin-vue-setup-extend 说明官方文档安装less测试less表达式安装vite-plugin-vue-setup-extend 说明 这里记录下自己在Vue3vite的项目使用less来写样式以及使用vite-plugin-vue-setup-extend直接定义组件…

【8】知识加工

一、概述 对信息抽取/知识融合后得到的“事实”进行知识推理以拓展现有知识、得到新知识。 知识加工主要包括三方面内容&#xff1a;本体构建、知识推理和质量评估。 二、本体构建 1.本体 定义&#xff1a;本体是用于描述一个领域的术语集合&#xff0c;其组织结构是层次结…

设计模式四:适配器模式

1、适配器模式的理解 适配器模式可以理解为有两个现成的类Adaptee和Target&#xff0c;它们两个是不能动的&#xff0c;要求必须使用B这个类来实现一个功能&#xff0c;但是A的内容是能复用的&#xff0c;这个时候我们需要编写一个转换器 适配器模式 Adaptee&#xff1a;被适…

Java Web(一)--介绍

Java Web 技术体系图 三大组成部分&#xff1a; 前端&#xff1a; 前端开发技术工具包括三要素&#xff1a;HTML、CSS 和 JavaScript&#xff1b;其他高级的前端框架&#xff0c;如bootstrap、jquery&#xff0c;VUE 等。 后端&#xff1a; 后端开发技术工具主要有&am…

剑指offer刷题笔记-链表

少年何妨梦摘星 敢挽桑弓射玉衡 解决与链表相关的问题总是有大量的指针操作&#xff0c;而指针操作的代码总是容易出错的。很多面试官喜欢出与链表相关的问题&#xff0c;就是想通过指针操作来考察应聘者的编码功底。 题目链接来自于 AcWing 、Leetcode&#xff08;LCR&#xf…

燃气企业须知 :智慧燃气信息化管理平台的作用 ?

关键词&#xff1a;智慧燃气、燃气信息化、智慧燃气平台、智慧燃气建设、智慧燃气解决方案 智慧燃气信息化管理平台通过城市输气管网作为主要基础&#xff0c;各个终端用户进行协调&#xff0c;依托信息化数据进行管理&#xff0c;从而让整个工作环节流程达到高效智能优势。通…

【LeetCode】树的BFS(层序遍历)精选6题

目录 1. N 叉树的层序遍历&#xff08;中等&#xff09; 2. 二叉树的锯齿形层序遍历&#xff08;中等&#xff09; 3. 二叉树的最大宽度&#xff08;中等&#xff09; 4. 在每个树行中找最大值&#xff08;中等&#xff09; 5. 找树左下角的值&#xff08;中等&#xff09…

leetcode(动态规划)53.最大子数组和(C++详细解释)DAY12

文章目录 1.题目示例提示 2.解答思路3.实现代码结果 4.总结 1.题目 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。 示例 提示 2.解答思…

数论 - 博弈论(Nim游戏)

文章目录 前言一、Nim游戏1.题目描述输入格式输出格式数据范围输入样例&#xff1a;输出样例&#xff1a; 2.算法 二、台阶-Nim游戏1.题目描述输入格式输出格式数据范围输入样例&#xff1a;输出样例&#xff1a; 2.算法 三、集合-Nim游戏1.题目描述输入格式输出格式数据范围输…

vue使用Nprogress进度条功能实现

下图中的这种顶部进度条是非常常见的&#xff0c;在vue项目中有对应的插件&#xff1a;Nprogress。 实现效果&#xff1a; csdn也在使用&#xff1a; 或者这样自己使用 1、安装 NProgress可以通过npm安装。 npm install --save nprogress 注意此处的--save等同于-s,就是将…

echats 时间直方图示例

需求背景 某订单有N个定时任务&#xff0c;每个任务的执行时间已经确定&#xff0c;希望直观的查看该订单的任务执行趋势 查询SQL&#xff1a; select UNIX_TIMESTAMP(DATE_FORMAT(exec_time,%Y-%m-%d %H:%i)) execTime, count(*) from order_detail_task where order_no 2…

【C++】vector模拟实现+迭代器失效

vector模拟实现 成员变量定义默认成员函数构造函数 迭代器范围for、对象类型匹配原则 容量操作sizeemptycapacityreserve成员变量未更新memcpy值拷贝 resize内置类型的构造函数 数据访问frontbackoperator[ ] 数据修改操作push_backpop_backswapclearinsertpos位置未更新无返回…

el-button 选择与非选择按钮批量处理

el-button 选择与非选择按钮批量处理 <el-button v-for"(voyage,i) in data[voyages][nowVoyage]":key"i"class"c-work-bts"type"primary":plain"nowWorkSpace!i"click"chooseWorkSpace(i)"size"small&qu…

C#快速配置NLog日志使用

首先我们需要在Nuget中安装Nlog和Nlog-Schema。 添加配置文件&#xff1a;NLog.config <?xml version"1.0" encoding"utf-8" ?> <nlog xmlns"http://www.nlog-project.org/schemas/NLog.xsd"xmlns:xsi"http://www.w3.org/2001…

CSS弹性布局

CSS弹性布局 一、概念 ​ 弹性盒子是 CSS3 的一种新的布局模式。 ​ CSS3 弹性盒&#xff08; Flexible Box 或 flexbox&#xff09;&#xff0c;是一种当页面需要适应不同的屏幕大小以及设备类型时确保元素拥有恰当的行为的布局方式。 ​ 引入弹性盒布局模型的目的是提供一…

山西电力市场日前价格预测【2024-02-21】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-02-21&#xff09;山西电力市场全天平均日前电价为470.29元/MWh。其中&#xff0c;最高日前电价为654.81元/MWh&#xff0c;预计出现在18:45。最低日前电价为355.63元/MWh&#xff0c;预计…

将Windows的系统日志自动收集并且转发到syslog服务器,百试百灵

将windows的系统日志自动收集并且转发到syslog服务器&#xff0c;百试百灵* **使用*Evtsys工具&#xff0c;他会自动收集windows系统日志&#xff0c;然后发送到syslog服务器&#xff0c;并且不乱码 下载链接&#xff1a;百度云永久链接 链接&#xff1a;https://pan.baidu.co…

D9741——用于也收路像机和笔记本电的等设备上的直流转换器。在便携式的仪器设备上。低电压输入时误操作保护电路, 定时闩锁、短路保护电路等功能

D9741是一块脉宽调制方三用于也收路像机和笔记本电的等设备上的直流转换器。在便携式的仪器设备上。 主要特点: 高精度基准电路 定时门锁、短路保护电路 低电压输入时误操作保护电路 输出基准电压(2.5V 超过工作范围能进行自动校正 封装形式: SOP16 应用: 电视摄像机 笔记本电…