2026/4/16 20:48:59
网站建设
项目流程
湘潭企业网站建设 磐石网络,南昌网站建设排行,php是做网站美工的吗,wordpress怎么用七牛上个月服务上线后#xff0c;用户反馈接口很慢#xff0c;平均响应时间2秒多。
排查了一圈#xff0c;发现是线程池配置不当导致的。
调优之后#xff0c;响应时间降到200ms#xff0c;记录一下完整过程。问题现象
用户反馈下单接口很慢#xff0c;看了下监控#xff1a…上个月服务上线后用户反馈接口很慢平均响应时间2秒多。排查了一圈发现是线程池配置不当导致的。调优之后响应时间降到200ms记录一下完整过程。问题现象用户反馈下单接口很慢看了下监控平均响应时间2.3秒P99响应时间5秒偶尔还会超时但CPU、内存、数据库都正常没有明显瓶颈。排查过程第一步看线程池状态用Arthas看了下线程池# 进入Arthasjava -jar arthas-boot.jar# 查看线程池状态thread -n3发现大量线程处于WAITING状态在等待任务。再看线程池的具体参数// 项目中的配置ThreadPoolExecutorexecutornewThreadPoolExecutor(10,// corePoolSize10,// maximumPoolSize60L,// keepAliveTimeTimeUnit.SECONDS,newLinkedBlockingQueue(10000)// 队列容量);问题找到了核心线程数只有10但队列容量是10000。第二步分析问题这个配置的问题核心线程数太小只有10个线程处理任务队列太大新任务会先进队列而不是创建新线程最大线程数等于核心线程数队列满了才会创建新线程但队列有10000容量几乎不会满结果高并发时任务在队列里排队等待响应时间自然就慢了。线程池工作原理先复习一下线程池的工作流程新任务到来 ↓ 当前线程数 corePoolSize ↓ 是 创建新线程执行 ↓ 否 队列未满 ↓ 是 放入队列等待 ↓ 否 当前线程数 maximumPoolSize ↓ 是 创建新线程执行 ↓ 否 执行拒绝策略关键点任务会优先进队列而不是创建新线程这就是为什么队列太大会导致响应慢——任务都在排队。优化方案方案1调整参数// 优化后的配置intcpuCoresRuntime.getRuntime().availableProcessors();ThreadPoolExecutorexecutornewThreadPoolExecutor(cpuCores*2,// corePoolSizeCPU核心数的2倍cpuCores*4,// maximumPoolSizeCPU核心数的4倍60L,TimeUnit.SECONDS,newLinkedBlockingQueue(100),// 队列容量减小newThreadPoolExecutor.CallerRunsPolicy()// 拒绝策略);参数计算公式CPU密集型corePoolSize CPU核心数 1IO密集型corePoolSize CPU核心数 * 2或更高我们的业务是IO密集型有数据库查询、RPC调用所以用CPU核心数 * 2。方案2使用SynchronousQueue如果想让任务尽快被执行可以用SynchronousQueueThreadPoolExecutorexecutornewThreadPoolExecutor(10,100,// 最大线程数调大60L,TimeUnit.SECONDS,newSynchronousQueue(),// 不缓存任务newThreadPoolExecutor.CallerRunsPolicy());SynchronousQueue不存储任务新任务来了直接创建线程执行。方案3动态线程池推荐更好的方案是动态调整线程池参数ComponentpublicclassDynamicThreadPool{privateThreadPoolExecutorexecutor;PostConstructpublicvoidinit(){executornewThreadPoolExecutor(20,50,60L,TimeUnit.SECONDS,newLinkedBlockingQueue(200),newThreadPoolExecutor.CallerRunsPolicy());}// 动态调整核心线程数publicvoidsetCorePoolSize(intsize){executor.setCorePoolSize(size);}// 动态调整最大线程数publicvoidsetMaxPoolSize(intsize){executor.setMaximumPoolSize(size);}// 获取线程池状态publicMapString,ObjectgetStatus(){MapString,ObjectstatusnewHashMap();status.put(corePoolSize,executor.getCorePoolSize());status.put(maximumPoolSize,executor.getMaximumPoolSize());status.put(activeCount,executor.getActiveCount());status.put(queueSize,executor.getQueue().size());status.put(completedTaskCount,executor.getCompletedTaskCount());returnstatus;}}配合配置中心Nacos/Apollo可以在线调整参数不用重启服务。优化后效果调整参数后对比数据指标优化前优化后核心线程数1032最大线程数1064队列容量10000200平均响应时间2.3秒180msP99响应时间5秒500ms效果响应时间降低了10倍以上。监控告警优化完不能不管了要加监控Scheduled(fixedRate60000)publicvoidmonitorThreadPool(){intactiveCountexecutor.getActiveCount();intqueueSizeexecutor.getQueue().size();intpoolSizeexecutor.getPoolSize();// 记录到监控系统log.info(ThreadPool status: active{}, queue{}, pool{},activeCount,queueSize,poolSize);// 队列积压告警if(queueSize100){alertService.send(线程池队列积压: queueSize);}// 线程数告警if(activeCountexecutor.getMaximumPoolSize()*0.8){alertService.send(线程池接近饱和: activeCount);}}常见错误配置错误1队列无界newLinkedBlockingQueue()// 默认是Integer.MAX_VALUE问题任务无限堆积最终OOM。错误2核心线程数太小corePoolSize5// 8核CPU只配5个核心线程问题CPU利用率低任务排队等待。错误3拒绝策略选错newThreadPoolExecutor.AbortPolicy()// 直接抛异常问题高并发时大量任务被拒绝用户看到报错。建议用CallerRunsPolicy让调用线程自己执行起到限流作用。线程池配置建议场景corePoolSizemaximumPoolSize队列CPU密集型N1N1小队列100以内IO密集型2N4N中等队列200-500混合型N*1.52N根据实际调整N CPU核心数远程排查技巧如果线上服务出问题需要远程查看线程池状态可以Arthasthread命令看线程状态JMX通过JMX远程连接查看自定义接口暴露线程池状态接口如果服务器在内网可以用星空组网工具把本地和服务器连起来直接用IDE的Remote Debug功能比看日志效率高很多。总结优化点说明增大核心线程数IO密集型用 CPU核心数*2减小队列容量避免任务积压合理设置最大线程数给突发流量留余地选对拒绝策略CallerRunsPolicy比较稳加监控告警及时发现问题核心原则让任务尽快被线程执行而不是在队列里排队。线程池配置踩过其他坑的欢迎评论区交流~