线程池监控

# 线程池监控

# 背景

系统时不时会出现短暂的响应慢,甚至偶尔超时,但监控面板上的CPU、内存、磁盘IO全都很正常。

排查发现: 订单服务的线程池悄悄"罢工"了 —— 核心线程全被占满,任务队列塞爆,新请求全部被拒绝,这种情况在常规监控中完全无法被发现!

解决方案: 引入线程池监控工具,支持动态伸缩,异常预警。

链路流程如下:

web请求->监控拦截器->线程池是否健康

  • 良好 实时数据
  • 警告 告警推送
  • 危险 自动扩容

# 使用方式

# 1、引入坐标

  <dependency>
    <groupId>cn.lovecyy</groupId>
    <artifactId>pool-monitor-starter</artifactId>
    <version>${version}</version>
</dependency>

# 2、application.yml

relaxed:
  thread-pool:
    monitor:
      #是否开启池监控
      monitorEnabled: true
      #预警阈值 超过则通知
      alertThreshold: 80
      alertChannels: email #通知方式 可自行选择处理
      monitorIntervalMills: 10000 #监控频率 毫秒单位
      adjustPoolNumEnabled: true #是否开启池自动伸缩
      #若阈值低于20% 且收缩空闲间隔超过,则将池恢复原池大小
      idleRatioMaxThreshold: 20 # 收缩阈值 低于20
      idleRatioIntervalMills: 60000 #收缩空闲间隔 毫秒单位
      adjustPoolMaxinumThreshold: 100 #最大扩展池大小

# 3、配置监控

增加 @ThreadPoolMonitor自定义注解,name不设置默认取bean名称

目前仅支持 ThreadPoolExecutor、ThreadPoolTaskExecutor线程池监控

	
    @ThreadPoolMonitor(name = "测试线程池")
	@Bean
	public ThreadPoolExecutor testThreadPool() {
		ThreadPoolExecutor orderProcessExecutor = new ThreadPoolExecutor(3, // 核心线程数
				5, // 最大线程数
				60, TimeUnit.SECONDS, // 空闲线程存活时间
				new LinkedBlockingQueue<>(200), // 工作队列
				new NamedThreadFactory("order-process-", false), new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
		);
		return orderProcessExecutor;
	}


# 4、单元测试

正常使用线程池即可

for (int i = 0; i < 1000; i++) {
			PoolOrder poolOrder = new PoolOrder();
			poolOrder.setUsername("username" + i);
			CompletableFuture.supplyAsync(() -> {
			// 处理订单逻辑
			return doProcessOrder(order);
		}, testThreadPool);
}

# 扩展说明

# 1、获取池信息

	private final ThreadPoolTaskMonitor monitor;

    /**
     * 获取当前池状态
     */
	@GetMapping("/stats")
	public List<ThreadPoolStats> getAllStats() {
		return monitor.getAllPoolStats();
	}
   /**
    * 获取周期内池平均趋势统计
    */
	@GetMapping("/trend")
	public Map<String, ThreadPoolTrend> getAllTrends() {
		Map<String, ThreadPoolTrend> trends = new HashMap<>();
		monitor.getAllPoolStats().forEach(stats -> {
			trends.put(stats.getPoolName(), monitor.getTrend(stats.getPoolName()));
		});
		return trends;
	}

# 2、自定义通知

实现AlertService注册成bean即可,默认的如下,不做任何处理

	@Bean
	public AlertService defaultAlertService() {
		return (finalMsg, channels) -> {
            //通知渠道根据yml配置的,多个,分割。
			// log.info("线程池告警,通知渠道:{},消息:{}",channels,finalMsg);
		};
	}

# 3、监控面版指标

监控面板展示的关键指标:

指标名称 健康值 警告值 危险值 当前值
活跃线程数 <70% 70%-85% >85% 56%
队列使用率 <60% 60%-80% >80% 23%
任务完成率 >95% 85%-95% <85% 99.8%
拒绝任务数 0 <10 ≥10 0
平均执行时间 <300ms 300ms-800ms >800ms 78ms

# 4、线程池预热

通过监控分析,可以了解到不同时间段,线程池的紧张状态。

针对这种情况,我们可以进行峰值提前预热,将线程池扩大。

解决方案:

开发一个定时任务,进行线程池的自动伸缩。 可以合理的调整高峰、次高峰、低峰时候的合理线程数。

代码实现:

获取ThreadPoolTaskMonitor的bean对象,从里面根据名称查询到线程池,调用对应方法调整池参数;