Hippo4j线程池实现技术

🔊博主介绍

🌟我是廖志伟,一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51CTO专家博主、阿里云专家博主、清华大学出版社签约作者、产品软文专业写手、技术文章评审老师、问卷调查设计师、个人社区创始人、开源项目贡献者。🌎跑过十五公里、🚀徒步爬过衡山、🔥有过三个月减肥20斤的经历、是个喜欢躺平的狠人。

📕拥有多年一线研发和团队管理经验,研究过主流框架的底层源码(Spring、SpringBoot、Spring MVC、SpringCould、Mybatis、Dubbo、Zookeeper),消息中间件底层架构原理(RabbitMQ、RockerMQ、Kafka)、Redis缓存、MySQL关系型数据库、 ElasticSearch全文搜索、MongoDB非关系型数据库、Apache ShardingSphere分库分表读写分离、设计模式、领域驱动DDD、Kubernetes容器编排等。

📙有过从0到1的项目高并发项目开发与管理经验,对JVM调优、MySQL调优、Redis调优 、ElasticSearch调优、消息中间件调优、系统架构调优都有着比较全面的实战经验。

📘有过云端搭建服务器环境,自动化部署CI/CD,弹性伸缩扩容服务器(最高200台),了解过秒级部署(阿里云的ACK和华为云的云容器引擎CCE)流程,能独立开发和部署整个后端服务,有过分库分表的实战经验。

🎥经过多年在CSDN创作上千篇文章的经验积累,我已经拥有了不错的写作技巧,与清华大学出版社签下了四本书籍的合约,并将陆续在明年出版。这些书籍包括了基础篇、进阶篇、架构篇的📌《Java项目实战—深入理解大型互联网企业通用技术》📌,以及📚《解密程序员的思维密码–沟通、演讲、思考的实践》📚。具体出版计划会根据实际情况进行调整,希望各位读者朋友能够多多支持!


🌾阅读前,快速浏览目录和章节概览可帮助了解文章结构、内容和作者的重点。了解自己希望从中获得什么样的知识或经验是非常重要的。建议在阅读时做笔记、思考问题、自我提问,以加深理解和吸收知识。

💡在这个美好的时刻,本人不再啰嗦废话,现在毫不拖延地进入文章所要讨论的主题。接下来,我将为大家呈现正文内容。

🥤本文内容

CSDN

部署

使用 Docker 运行服务端,默认使用内置 H2 数据库,数据持久化到 Docker 容器存储卷中。

docker run -d -p 6691:6691 --name hippo4j-server \
-e DATASOURCE_MODE=mysql \
-e DATASOURCE_HOST=192.168.3.200 \
-e DATASOURCE_PORT=3306 \
-e DATASOURCE_DB=hippo4j_manager \
-e DATASOURCE_USERNAME=root \
-e DATASOURCE_PASSWORD=root \
hippo4j/hippo4j-server

访问 Server 控制台,路径 http://hadoop3:6691/index.html,默认用户名密码:admin / 123456
在这里插入图片描述

运行模式

Hippo4j 分为两种使用模式:轻量级依赖配置中心以及无中间件依赖版本。
在这里插入图片描述
Hippo4jConfig系统:它提供了极具可行性且灵活的动态线程池管理方案。利用了Nacos、Apollo、Zookeeper、ETCD、Polaris和Consul等多元化的第三方配置中心这些特性,可根据用户需求完成线程池参数的实时调整与更新,从而实现高度智能化的线程池管理功能。此外,该系统还具备实时预警及监控等独特功能,使得整个系统的稳定性得以提高并大幅提升表现效果。

Hippo4jServer服务器:在此基础上进行扩展,并采取了无需借助任何中介软件的全新发布方式,大大降低了系统依赖成本。其主要工作原理是由用户通过直观易懂的Web界面去直接操作线程池的建立、变更以及状态的浏览等任务,由此最大限度地简化了操作流程,减轻了用户负担。虽然Hippo4jServer在各项功能上都远超过Hippo4jConfig,但是在保持稳定性的同时,复杂度也相应有所增加。这就需要在服务器端额外安装一款Java程序以便运行,并且还依赖MySQL数据库作为数据存储设施。

集成

<dependency>
    <groupId>cn.hippo4j</groupId>
    <artifactId>hippo4j-config-spring-boot-starter</artifactId>
    <version>1.5.0</version>
</dependency>

请在应用程序启动类中引入注解@EnablesDynamicThreadPool以实现动态线程池功能。

@SpringBootApplication
@EnableDynamicThreadPool
public class ExampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(ExampleApplication.class, args);
    }
}

请参照示例项目hippo4j-config-nacos-spring-boot-starter-example进行配置调整,具体步骤包括:
1)设定Nacos服务的URL;
2)在Nacos所关联的空间和分组内创建名为hippo4j-nacos.properties的配置文件;
3)将原先在bootstrap.properties文件中的相关配置参数迁移至上述新创配置文件之中。

spring.dynamic.thread-pool.enable=true  # 启用动态线程池
spring.dynamic.thread-pool.banner=true  # 启用线程池横幅
spring.dynamic.thread-pool.check-state-interval=5  # 线程池状态检查间隔(秒)
spring.dynamic.thread-pool.monitor.enable=true  # 启用线程池监控
spring.dynamic.thread-pool.monitor.collect-types=micrometer  # 监控数据收集类型
spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic,web  # 监控的线程池类型
spring.dynamic.thread-pool.monitor.initial-delay=10000  # 监控延迟启动时间(毫秒)
spring.dynamic.thread-pool.monitor.collect-interval=5000  # 监控数据采集间隔(毫秒)
spring.dynamic.thread-pool.notify-platforms[0].platform=WECHAT  # 通知平台:微信
spring.dynamic.thread-pool.notify-platforms[0].token=ac0426a5-c712-474c-9bff-72b8b8f5caff  # 微信通知的令牌
spring.dynamic.thread-pool.notify-platforms[1].platform=DING  # 通知平台:钉钉
spring.dynamic.thread-pool.notify-platforms[1].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55  # 钉钉通知的令牌
spring.dynamic.thread-pool.notify-platforms[2].platform=LARK  # 通知平台:飞书
spring.dynamic.thread-pool.notify-platforms[2].token=2cbf2808-3839-4c26-a04d-fd201dd51f9e  # 飞书通知的令牌
spring.dynamic.thread-pool.executors[0].thread-pool-id=message-consume  # 线程池ID:消息消费
spring.dynamic.thread-pool.executors[0].thread-name-prefix=message-consume  # 线程名前缀:消息消费
spring.dynamic.thread-pool.executors[0].core-pool-size=4  # 核心线程数
spring.dynamic.thread-pool.executors[0].maximum-pool-size=6  # 最大线程数
spring.dynamic.thread-pool.executors[0].queue-capacity=512  # 队列容量
spring.dynamic.thread-pool.executors[0].blocking-queue=ResizableCapacityLinkedBlockingQueue  # 阻塞队列类型
spring.dynamic.thread-pool.executors[0].execute-time-out=800  # 任务执行超时时间(毫秒)
spring.dynamic.thread-pool.executors[0].rejected-handler=AbortPolicy  # 拒绝策略
spring.dynamic.thread-pool.executors[0].keep-alive-time=6691  # 空闲线程存活时间(毫秒)
spring.dynamic.thread-pool.executors[0].allow-core-thread-time-out=true  # 是否允许核心线程超时销毁
spring.dynamic.thread-pool.executors[0].alarm=true  # 是否启用告警
spring.dynamic.thread-pool.executors[0].active-alarm=80  # 活跃线程告警阈值(百分比)
spring.dynamic.thread-pool.executors[0].capacity-alarm=80  # 队列容量告警阈值(百分比)
spring.dynamic.thread-pool.executors[0].notify.interval=8  # 告警通知间隔(秒)
spring.dynamic.thread-pool.executors[0].notify.receives=chen.ma  # 告警通知接收人
spring.dynamic.thread-pool.executors[1].thread-pool-id=message-produce  # 线程池ID:消息生产
spring.dynamic.thread-pool.executors[1].thread-name-prefix=message-produce  # 线程名前缀:消息生产
spring.dynamic.thread-pool.executors[1].core-pool-size=2  # 核心线程数
spring.dynamic.thread-pool.executors[1].maximum-pool-size=4  # 最大线程数
spring.dynamic.thread-pool.executors[1].queue-capacity=1024  # 队列容量
spring.dynamic.thread-pool.executors[1].blocking-queue=ResizableCapacityLinkedBlockingQueue  # 阻塞队列类型
spring.dynamic.thread-pool.executors[1].execute-time-out=800  # 任务执行超时时间(毫秒)
spring.dynamic.thread-pool.executors[1].rejected-handler=AbortPolicy  # 拒绝策略
spring.dynamic.thread-pool.executors[1].keep-alive-time=6691  # 空闲线程存活时间(毫秒)
spring.dynamic.thread-pool.executors[1].allow-core-thread-time-out=true  # 是否允许核心线程超时销毁
spring.dynamic.thread-pool.executors[1].alarm=true  # 是否启用告警
spring.dynamic.thread-pool.executors[1].active-alarm=80  # 活跃线程告警阈值(百分比)
spring.dynamic.thread-pool.executors[1].capacity-alarm=80  # 队列容量告警阈值(百分比)
spring.dynamic.thread-pool.executors[1].notify.interval=8  # 告警通知间隔(秒)
spring.dynamic.thread-pool.executors[1].notify.receives=chen.ma  # 告警通知接收人

在这里插入图片描述
如果是yaml文件,SpringBoot 应用配置文件添加::

server:
  port: 8090
  servlet:
    context-path: /example
spring:
  profiles:
    active: dev
  dynamic:
    thread-pool:
      # 是否开启动态线程池
      enable: true
      # 是否打印 banner
      banner: true
      # 是否开启线程池数据采集,对接 MicrometerESLog 等
      collect: true
      # 检查线程池状态,是否达到报警条件,单位毫秒
      check-state-interval: 3000
      # 通知报警平台,请替换为自己创建的群机器人
      notify-platforms:
        - platform: 'WECHAT'
          token: xxx
        - platform: 'DING'
          token: xxx
          secret: xxx  # 加签专属
        - platform: 'LARK'
          token: xxx
      # NacosApolloZookeeperETCDPolarisConsul 任选其一
      nacos:
        data-id: xxx
        group: xxx
      apollo:
        namespace: xxxx
      # 配置中心文件格式
      config-file-type: yml
      # 支持 tomcat、undertow、jetty 三种容器线程池
      web:
        core-pool-size: 100
        maximum-pool-size: 200
        keep-alive-time: 1000
      # 全局通知配置-是否报警
      alarm: true
      # 活跃度报警阈值;假设线程池最大线程数 10,当线程数达到 8 发起报警
      active-alarm: 80
      # 容量报警阈值;假设阻塞队列容量 100,当容量达到 80 发起报警
      capacity-alarm: 80
      # 报警间隔,同一线程池下同一报警纬度,在 interval 时间内只会报警一次,单位秒
      alarm-interval: 8
      # 企业微信填写用户 ID(填写其它将无法达到 @ 效果)、钉钉填手机号、飞书填 ou_ 开头唯一 ID
      receives: xxx
      # 动态线程池列表
      executors:
        - thread-pool-id: 'message-consume'
          # 核心线程数
          core-pool-size: 1
          # 最大线程数
          maximum-pool-size: 1
          # 阻塞队列名称,参考 BlockingQueueTypeEnum,支持 SPI
          blocking-queue: 'LinkedBlockingQueue'
          # 阻塞队列大小
          queue-capacity: 1
          # 执行超时时间,超过此时间发起报警,单位毫秒
          execute-time-out: 1000
          # 拒绝策略名称,参考 RejectedPolicyTypeEnum,支持 SPI
          rejected-handler: 'AbortPolicy'
          # 线程存活时间,单位秒
          keep-alive-time: 1024
          # 是否允许核心线程超时
          allow-core-thread-time-out: true
          # 线程工厂名称前缀
          thread-name-prefix: 'message-consume'
          # 是否报警
          alarm: true
          # 活跃度报警阈值;假设线程池最大线程数 10,当线程数达到 8 发起报警
          active-alarm: 80
          # 容量报警阈值;假设阻塞队列容量 100,当容量达到 80 发起报警
          capacity-alarm: 80
          # 通知配置,线程池中通知配置如果存在,则会覆盖全局通知配置
          notify:
            # 报警间隔,同一线程池下同一报警纬度,在 interval 时间内只会报警一次,单位分钟
            interval: 8
            # 企业微信填写用户 ID(填写其它将无法达到 @ 效果)、钉钉填手机号、飞书填 ou_ 开头唯一 ID
            receives: xxx
        - thread-pool-id: 'message-produce'
          core-pool-size: 1
          maximum-pool-size: 1
          queue-capacity: 1
          execute-time-out: 1000
          blocking-queue: 'LinkedBlockingQueue'
          rejected-handler: 'AbortPolicy'
          keep-alive-time: 1024
          allow-core-thread-time-out: true
          thread-name-prefix: 'message-consume'
          alarm: true
          active-alarm: 80
          capacity-alarm: 80
          notify:
            interval: 8
            receives: xxx

在启动Hippo4j-Config-Nacos-Spring-Boot-Starter-Example工程的 ConfigNacosExampleApplication时,我们需要对Nacos配置中的hippo4j-nacos.properties文件进行一定的修改操作。如此一来,将在控制台上观察到日志输出中包含了有关线程池信息的变动信息。

根据ThreadPoolExecutor的特性,我们必须增加一个新的线程池配置类,并同时使用@DynamicThreadPool注解加以标识。其中,“threadPoolId"字段代表了服务器端所创建的线程池ID。这也是先前在配置文件中设定的参数值,即"spring.dynamic.thread-pool.executors[0].thread-pool-id=message-consume”。

个性化定制方面,Hippo4j-Config主要依赖于配置中心实现线程池配置的动态更改。然而,现阶段在这种模式下存在着一些不足之处,例如:当配置文件发生改变时,会导致所有客户端为了适应变更而重新加载数据。因此,我们希望Hippo4j-Config能够具备如同Hippo4j-Server那样,支持客户端集群个性化配置的能力,以便我们能针对单一客户端进行独立的配置变更调整。

ThreadPoolExecutor 适配
添加线程池配置类,通过 @DynamicThreadPool 注解修饰。threadPoolId 为服务端创建的线程池 ID。

import cn.hippo4j.core.executor.DynamicThreadPool;
import cn.hippo4j.core.executor.support.ThreadPoolBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
public class ThreadPoolConfig {

    @Bean
    @DynamicThreadPool
    public ThreadPoolExecutor messageConsumeDynamicExecutor() {
        String threadPoolId = "message-consume";
        ThreadPoolExecutor messageConsumeDynamicExecutor = ThreadPoolBuilder.builder()
                .threadFactory(threadPoolId)
                .threadPoolId(threadPoolId)
                .dynamicPool()
                .build();
        return messageConsumeDynamicExecutor;
    }

    @Bean
    @DynamicThreadPool
    public ThreadPoolExecutor messageProduceDynamicExecutor() {
        String threadPoolId = "message-produce";
        ThreadPoolExecutor messageProduceDynamicExecutor = ThreadPoolBuilder.builder()
                .threadFactory(threadPoolId)
                .threadPoolId(threadPoolId)
                .dynamicPool()
                .build();
        return messageProduceDynamicExecutor;
    }

}

通过 ThreadPoolBuilder 构建动态线程池,只有 threadFactory、threadPoolId 为必填项,其它参数会从配置中心拉取。

项目中使用上述定义的动态线程池,如下所示:

@Resource
private ThreadPoolExecutor messageConsumeDynamicExecutor;

messageConsumeDynamicExecutor.execute(() -> xxx);

@Resource
private ThreadPoolExecutor messageProduceDynamicExecutor;

messageProduceDynamicExecutor.execute(() -> xxx);

线程池监控配置

监控前置条件:需要先完成 hippo4j-config 的 接入工作。

接下来引入 SpringBoot Actuator。Spring 2.x 一般都有版本指定,所以这里不用写版本号。

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

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

添加动态线程池监控相关配置:

management:
  metrics:
    export:
      prometheus:
        enabled: true
  server:
    port: 29999 # 可选配置,如果不配置该 port,直接使用 ${server.port}
  endpoints:
    web:
      exposure:
        include: '*' # 测试使用,开启了所有端点,生产环境不建议 *
spring:
  dynamic:
    thread-pool:
      monitor:
        enable: true # 是否开启采集线程池运行时数据
        collect-interval: 5000 # 采集线程池运行数据频率
        collect-types: micrometer # 采集线程池运行数据的类型。eg:log、micrometer。多个可以同时使用,默认 micrometer
        initial-delay: 10000 # 项目启动后延迟多久进行采集
        thread-pool-types: dynamic # 采集线程池的类型。eg:dynamic、web、adapter。可任意配置,默认 dynamic

项目启动,访问 http://localhost:29999/actuator/prometheus 出现 dynamic_thread_pool_ 前缀的指标,即为成功。
在这里插入图片描述
配置 Prometheus
通过 Docker 启动 Prometheus 服务。

docker run -d -p 9090:9090 --name prometheus prom/prometheus

添加 Prometheus 抽取数据任务。
进入 prometheus 容器内部,编辑 prometheus 配置文件

docker exec -it prometheus /bin/sh
vi /etc/prometheus/prometheus.yml

scrape_configs 节点下新添加一个 job,如果 Prometheus 是 Docker 方式部署,{scrape_configs.static_configs.targets} 需要写本机的 IP。

scrape_configs:
  - job_name: 'dynamic-thread-pool-job'
    scrape_interval: 5s
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: [ '127.0.0.1:29999' ]

配置成功后 exit 退出容器,并进行 Prometheus 容器重启 docker restart prometheus。

访问 Prometheus 控制台 http://localhost:9090/graph 路径,能够展示相关指标即为配置成功。
在这里插入图片描述配置 Grafana

docker run -d -p 3000:3000 --name=grafana grafana/grafana

访问 Grafana 地址,http://localhost:3000 用户名密码:admin

Grafana 访问 http://localhost:3000/datasources 导入 Prometheus 数据源。

在这里插入图片描述
如果 Prometheus 为 Docker 方式部署,HTTP URL 需要为本地 IP,比如:http://192.168.1.5:9090

参数默认配置

项目中线程池一多,配置文件中配置就显得很臃肿。为此 hippo4j-config 开发出了动态线程池默认配置。

spring:
  dynamic:
    thread-pool:
      default-executor:
        core-pool-size: 4
        maximum-pool-size: 6
        blocking-queue: ResizableCapacityLinkedBlockingQueue
        queue-capacity: 1024
        execute-time-out: 1000
        keep-alive-time: 9999
        rejected-handler: AbortPolicy
        active-alarm: 90
        capacity-alarm: 85
        alarm: true
        allow-core-thread-time-out: true
        notify:
          interval: 5
          receives: chen.ma
      executors:
        - thread-pool-id: message-produce
        - thread-pool-id: message-consume
          core-pool-size: 80
          maximum-pool-size: 100
          execute-time-out: 1000
          notify:
            interval: 6
            receives: chen.ma

CSDN

📢文章总结

对本篇文章进行总结:

🔔以上就是今天要讲的内容,阅读结束后,反思和总结所学内容,并尝试应用到现实中,有助于深化理解和应用知识。与朋友或同事分享所读内容,讨论细节并获得反馈,也有助于加深对知识的理解和吸收。

以梦为马,不负韶华

🔔如果您需要转载或者搬运这篇文章的话,非常欢迎您私信我哦~

🚀🎉希望各位读者大大多多支持用心写文章的博主,现在时代变了,🚀🎉 信息爆炸,酒香也怕巷子深🔥,博主真的需要大家的帮助才能在这片海洋中继续发光发热🎨,所以,🏃💨赶紧动动你的小手,点波关注❤️,点波赞👍,点波收藏⭐,甚至点波评论✍️,都是对博主最好的支持和鼓励!

📥博主目标

探寻内心世界,博主分享人生感悟与未来目标

  • 🍋程序开发这条路不能停,停下来容易被淘汰掉,吃不了自律的苦,就要受平庸的罪,持续的能力才能带来持续的自信。我本是一个很普通的程序员,放在人堆里,除了与生俱来的盛世美颜,就剩180的大高个了,就是我这样的一个人,默默写博文也有好多年了。
  • 📺有句老话说的好,牛逼之前都是傻逼式的坚持,希望自己可以通过大量的作品、时间的积累、个人魅力、运气、时机,可以打造属于自己的技术影响力。
  • 💥内心起伏不定,我时而激动,时而沉思。我希望自己能成为一个综合性人才,具备技术、业务和管理方面的精湛技能。我想成为产品架构路线的总设计师,团队的指挥者,技术团队的中流砥柱,企业战略和资本规划的实战专家。
  • 🎉这个目标的实现需要不懈的努力和持续的成长,但我必须努力追求。因为我知道,只有成为这样的人才,我才能在职业生涯中不断前进并为企业的发展带来真正的价值。在这个不断变化的时代,我们必须随时准备好迎接挑战,不断学习和探索新的领域,才能不断地向前推进。我坚信,只要我不断努力,我一定会达到自己的目标。

🔔有需要对自己进行综合性评估,进行职业方向规划,我可以让技术大牛帮你模拟面试、针对性的指导、传授面试技巧、简历优化、进行技术问题答疑等服务。

可访问:https://java_wxid.gitee.io/tojson/

开发人员简历优化、面试突击指导、技术问题解答

相关推荐

  1. 简易线实现

    2024-04-02 17:40:01       19 阅读
  2. 网路服务器——线技术

    2024-04-02 17:40:01       31 阅读
  3. Go 线实现案例

    2024-04-02 17:40:01       27 阅读
  4. 简易线实现

    2024-04-02 17:40:01       20 阅读
  5. 线的参数 以及实现

    2024-04-02 17:40:01       17 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-02 17:40:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-02 17:40:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-02 17:40:01       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-02 17:40:01       20 阅读

热门阅读

  1. c语言实现循环位移的函数

    2024-04-02 17:40:01       14 阅读
  2. 深入了解Flutter中的Sealed Class及其使用

    2024-04-02 17:40:01       13 阅读
  3. linux脚本打包

    2024-04-02 17:40:01       12 阅读
  4. python系列教程220——哪种迭代最快

    2024-04-02 17:40:01       13 阅读
  5. 问答系统开发:基于深度学习的文本理解与生成

    2024-04-02 17:40:01       12 阅读
  6. 【leetcode面试经典150题】2.移除元素(C++)

    2024-04-02 17:40:01       9 阅读
  7. R语言提取站点的nc文件时间序列数据

    2024-04-02 17:40:01       13 阅读
  8. 每日一题 --- 用栈实现队列[力扣][Go]

    2024-04-02 17:40:01       13 阅读