利用 Nacos 实现了一个动态化线程池,非常实用

news2024/12/27 16:20:09

在后台开发中,会经常用到线程池技术,对于线程池核心参数的配置很大程度上依靠经验。然而,由于系统运行过程中存在的不确定性,我们很难一劳永逸地规划一个合理的线程池参数。在对线程池配置参数进行调整时,一般需要对服务进行重启,这样修改的成本就会偏高。一种解决办法就是,将线程池的配置放到平台侧,运行开发同学根据系统运行情况对核心参数进行动态配置。

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

代码实现

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: 8010
  # 应用名称(nacos会将该名称当做服务名称)
spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        namespace: public
        server-addr: 192.168.174.129:8848
      config:
        server-addr: 192.168.174.129: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:8010/threadpool/print打印当前线程池的配置。

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

访问http://localhost:8010/threadpool/add?count=20增加20个任务,重新打印线程池配置

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

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

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

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

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

总结

这里,只是简单实现了一个可以调整核心线程数和最大线程数的动态线程池。具体的线程池实现原理可以参考美团的这篇文章:https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html,结合监控告警等实现一个完善的动态线程池产品。

优秀的轮子还有好多,比如Hippo4J ,使用起来和dynamic-tp差不多。Hippo4J 有无依赖中间件实现动静线程池,也有默认实现Nacos和Apollo的版本,而dynamic-tp 默认实现依赖Nacos或Apollo。

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

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

相关文章

SegeX Automation:VC调用Golden Surfer自动化失败(VC调用Automation失败)原因详解

----哆啦刘小洋 原创&#xff0c;转载需说明出处 2022-12-29 SegeX Automation:Surfer自动化&#xff08;Automation&#xff09;失败原因1 简介2 初始化Surfer对象不成功2.1 一般代码2.1 改进代码3 Windows系统原因4 Surfer原因5 其他问题1 简介 在工程领域&#xff0c;Golde…

国产无线耳机什么牌子好?国产真无线蓝牙耳机排行

随着近几年蓝牙耳机市场的快速发展&#xff0c;蓝牙耳机依旧占据着半个行业的份额&#xff0c;在人们的日常生活中出现的频率也越来越高。叫得上名的国产蓝牙耳机牌子也越来越多&#xff0c;那么&#xff0c;国产无线耳机什么牌子好&#xff1f;下面&#xff0c;我来给大家推荐…

无代码资讯 | Gartner 预测明年低代码市场规模;微软推出低代码学习平台;AWS 推出开发无服务器应用的低代码平台

栏目导读&#xff1a;无代码资讯栏目从全球视角出发&#xff0c;带您了解无代码相关最新资讯。 TOP3 大事件 1、Gartner 预测到 2023 年&#xff0c;全球低代码市场预计达到269亿美元 根据 Gartner 的最新预测&#xff0c;到 2023 年全球低代码开发技术市场规模预计将达到 269…

hnu计网实验三-应用层和传输层协议分析(PacketTracer)

前言&#xff1a;难度适中的一个实验 一、实验目的 通过本实验&#xff0c;熟悉PacketTracer的使用&#xff0c;学习在PacketTracer中仿真分析应用层和传输层协议&#xff0c;进一步加深对协议工作过程的理解。 二、实验内容 研究应用层和传输层协议 从 PC 使用 URL 捕获 W…

多道程序与分时多任务--rCore[3]

概念 协作式操作系统 计算机硬件在快速发展&#xff0c;内存容量在逐渐增大&#xff0c;处理器的速度也在增加&#xff0c;外设 I/O 性能方面的进展不大。于是就想到&#xff0c;让应用在执行 I/O 操作或空闲时&#xff0c;可以主动 释放处理器 &#xff0c;让其他应用继续执行…

Linux- 系统随你玩之--grep查找文件内容

1、前言 我们在使用过程中经常需要对当前获取的文件进行过滤、提取和分析&#xff0c;以便快速查找到含有指定内容的文件或指定信息的那些行。本章我们继续深入了解有关文本检索的内容。 2、grep 查找文件内容 Linux系统中grep命令是一种强大的文本搜索工具&#xff0c;它能…

使用Loki收集网络设备日志

新钛云服已累计为您分享716篇技术干货前言Loki是Grafana Labs团队的开源项目&#xff0c;是一个水平可扩展&#xff0c;高可用性&#xff0c;多租户的日志聚合系统&#xff0c;它的设计非常简洁易于操作。受Prometheus启发的&#xff0c;可以水平扩展、高可用以及支持多租户的日…

S-HUB如何实现数据库对接WEBSERVICE API

近几年SAAS被大家逐步认可&#xff0c;越来越多的企业购买SAAS服务&#xff0c;但是仍然有很多自建各种业务系统&#xff0c;这些系统建设时一般都只考虑自身业务&#xff0c;而不是特别注重与别的系统之间的互联互通。而企业为了提升效率&#xff0c;又需要将这些业务与别的系…

Java开发 - 双向链表不可怕

前言 说起链表&#xff0c;那还是当初上学的时候学习的&#xff0c;印象里就觉得像锁链一样一环扣一环&#xff0c;后来工作后就几乎没实际接触过链表&#xff0c;每当遇到链表&#xff0c;总是不知道该怎么讲&#xff0c;因为对链表的本质一无所知。也是在学习了Java后&#…

剑指offer----C语言版----第三天

目录 1. 替换空格 1.1 题目描述 1.2 题目背景 1.3 必要的思考 1.4 思路一 1.5 思路二 1.6 思路三&#xff08;学方法&#xff09; 1.7 小试牛刀 1. 替换空格 原题链接&#xff1a;剑指 Offer 05. 替换空格 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/p…

回首2022,展望2023(年度总结)

回首2022&#xff0c;展望2023 热爱技术的伙计们&#xff1a; 哈喽&#xff0c;这篇文章经过一周左右的写写改改&#xff0c;在2022最后的工作日与大家见面&#xff0c;有人说总结是为了更好的开始。是的&#xff0c;一年的时间真的很快&#xff0c;马上就是2023了&#xff0…

k-mean聚类的python实现

文章目录介绍KMeans()函数介绍实例导入相关包整理数据手肘法确定分类个数创建模型绘制结果分为3类的结果作者&#xff1a;张双双介绍 sklearn.cluster模块提供了常用的非监督聚类算法。 该模块中每一个聚类算法都有两个变体: 一个是类(class)另一个是函数(function)。 类实现了…

day06 Debug基础练习

1.Debug模式 1.1 什么是Debug模式 是供程序员使用的程序调试工具&#xff0c;它可以用于查看程序的执行流程&#xff0c;也可以用于追踪程序执行过程来调试程序。 1.2 Debug介绍与操作流程 如何加断点 选择要设置断点的代码行&#xff0c;在行号的区域后面单击鼠标左键即可 …

刷题记录:牛客NC24858Job Hunt [最长路+两种不同判环详解]

传送门:牛客 题目描述: 奶牛们正在找工作。农场主约翰知道后&#xff0c;鼓励奶牛们四处碰碰运气。而且他还加了一条要求&#xff1a;一头牛在一个城市 最多只能赚D&#xff08;1≤D≤1000&#xff09;美元&#xff0c;然后它必须到另一座城市工作。当然&#xff0c;它可以在…

2自由度陀螺仪滑模控制和PID控制跟踪目标轨迹

目录 前言 1.陀螺仪模型 2.滑模跟踪控制 3.PID控制 4.总结 前言 不为别的&#xff0c;这篇小文章只为内涵某人&#xff0c;问候一下他&#xff1a;不是有手就行&#xff0c;哈哈~ 1.陀螺仪模型 2.滑模跟踪控制 对于2自由度陀螺仪有两个方向x、y跟踪&#xff0c;所以需要…

对于软件测试认识的几大误区,看看你有没有?

对于软件开发来说&#xff0c;软件测试可能还不被大众了解&#xff0c;很多开发人员&#xff0c;包括很多软件高层管理人员&#xff0c;由于缺乏软件测试的知识和实践经验&#xff0c;对软件测试的认识还有很多的误区&#xff1a; 误区一&#xff1a;如果软件发布后有质量问题…

我们一起来谈谈高并发和分布式系统的幂等如何处理!

什么是幂等性 幂等是一个数学与计算机学概念&#xff0c;在数学中某一元运算为幂等时&#xff0c;其作用在任一元素两次后会和其作用一次的结果相同。 在计算机中编程中&#xff0c;一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。 幂等函数或幂等…

Codeforces Round #841 (Div. 2)

题目链接 A. Joey Takes Money 题目描述 input output 题意&#xff1a; 有一个长度为n的数组&#xff0c;你可以进行一步操作&#xff0c;选择两个i,j&#xff0c;然后再选择两个数x,y&#xff0c;使得a[i]a[j] xy&#xff0c;然后将a[i] x,a[j] y&#xff0c;问任意步操…

Spring 中运用的 9 种设计模式

Spring中涉及的设计模式总结&#xff0c;在面试中也会经常问道 Spring 中设计模式的问题。本文以实现方式、实质、实现原理的结构简单介绍 Sping 中应用的 9 种设计模型&#xff0c;具体详细的刨析会在后面的文章发布&#xff0c;话不多说&#xff0c;来个转发、在看、收藏三连…

京东软件测试岗:惨不忍睹的三面,幸好做足了准备,月薪17k,已拿offer

我今年25岁&#xff0c;专业是电子信息工程本科&#xff0c;19年年末的时候去面试&#xff0c;统一投了测试的岗位&#xff0c;软件硬件都有&#xff0c;那时候面试的两家公司都是做培训的&#xff0c;当初没啥钱&#xff0c;他们以面试为谎言再推荐去培训这点让我特别难受。 …