记一次CPU占用持续上升问题排查(Nacos动态路由引起)

文章描述了一个关于IC服务在使用Nacos动态路由后CPU占用率逐渐升高的问题。经过排查,问题出在频繁调用NacosConfigService创建新线程,解决方案是采用单例模式限制实例的创建,从而降低CPU消耗。作者还分享了使用jstack等工具进行问题诊断的过程。

1、问题描述

在整个项目安装完成测试后一个周的时间里,IC服务(我们自己的服务名称)从刚开始CPU占用百分之一涨到了百分之六十左右,而且在持续上升,只有重启IC服务才会降下去,但是时间一长还是会涨上来;经过确认只有IC服务有这个问题,且注意到只有IC会动态修改nacos中的路由信息,然后就把锅甩到了Nacos上;

2、使用版本与动态路由实现

2.1、使用版本

		<!--gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- SpringCloud Ailibaba Nacos Config -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

2.2、动态路由实现

动态路由实现可参考
https://blog.youkuaiyun.com/crf_c/article/details/128789947?spm=1001.2014.3001.5502

3、问题排除

3.1 服务器上排查

首先想到的是利用 top 命令来查看具体进程运行信息, 可以查到占用CPU较大的进程的pid,可以看到pid是23170的进程占用资源最大,然后根据 ps -ef|grep java 命令可以找到是哪个服务占用较大,缩小范围。(只是示例,并不是真是场景)
图片
服务
然后就是使用ps H -eo pid,tid,%cpu | grep 23170 可以查看到 23170这个进程里对应线程
占用多少CPU,找到最高的一个,由于我的是假数据,随便来用一个tid:23546在这里插入图片描述
后面再使用jstack pid命令来看他线程栈的信息,会得到类似这种信息,可以看到下面信息中有tid,nid相关信息,但是是以16进制显示的,使用 printf %x 23456 得出对应的十六进制值在下面寻找,

"com.alibaba.nacos.client.config.security.updater" #2269 daemon prio=5 os_prio=0 tid=0x00007fa3ec401800 nid=0x8d85 waiting on condition [0x00007fa314396000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000000f7f3eae0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

但是上面这个提示信息 显示 是 线程内部的,而且是nacos client 内部的,只是确定了范围是在nacos,还是定位不到具体代码。

3.2 代码里面排查

然后就没办法了,只能代码里全局搜索com.alibaba.nacos.client.config.security.updater这个类,显示是ServerHttpAgent 类的方法
在这里插入图片描述
创建了一个线程,构造方法应该是初始化时候只用调用一次就可以了。然后往上找,找到NacosConfigService 类中调用了,然后自己的业务里面又调用了NacosConfigService
在这里插入图片描述
然后就发现以前人写的时候是直接创建的,而且定时任务,接口获取一次配置,发布一次配置,调用了这两个方法,一直在创建线程,找到原因了,也很好解决;打算前人挖坑,后人埋坑,真的很烦;
在这里插入图片描述

4、问题原因,解决方案

4.1、原因

业务逻辑一直频繁调用发布配置相关代码,导致一直创建NacosConfigService,从而导致一直在创建线程,导致CPU一直上升

4.2、解决方案

解决很简单,写个单例就行,后续获取直接getInstance()获取;示例代码如下:

package com.cherf.ic.common.nacos;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.Properties;

/**
 * @author cherf
 * @description: NacosConfigservice单例
 * @date 2022/09/19 16:35
 **/
@Component
public class NacosConfigService {
    private static final Log log = LogFactory.get(NacosConfigService.class);
    /**
     * nacos地址
     */
    @Value("${spring.cloud.nacos.discovery.server-addr}")
    private  String ipAddress;

    //声明变量, 使用volatile关键字确保绝对线程安全
    private volatile  ConfigService configService = null;

    @Bean
    public  ConfigService getInstance() throws NacosException {
        //判断实例是否null
        if (configService == null) {
            //对单例类进行加锁
            synchronized (NacosConfigService.class) {
                //在判断是否为null
                if (configService == null) {
                    Properties properties = new Properties();
                    // nacos服务器地址,127.0.0.1:8848
                    properties.put(PropertyKeyConst.SERVER_ADDR, ipAddress);
                    //创建实例
                    configService = NacosFactory.createConfigService(properties);
                    log.info("==========创建configService实例===============");
                }
            }
        }
        //返回实例
        return configService;
    }
}

获取示例在这里插入图片描述

5、总结

虽然是一个很小的点,但是也困扰,定位了很久,最后才发现,也尝试使用了阿里的Arthas,也很不错,可以试试,欢迎讨论!

参考大佬:记一次 Nacos 导致的 CPU 飙高问题 !

<think>嗯,用户需要一套针对五年经验Java工程师的面试题预测,结合当前就业环境。从对话历史看,用户似乎正在准备面试,且已经掌握了一些基础面试题(如JVM、并发等),现在需要更深入、更贴合阶岗位的问题。用户特别强调“当前就业环境”,说明ta注意到2024年市场对Java工程师的要求在提——企业更关注技术深度、架构能力和实战经验,而非单纯使用框架的能力。引用[5]提到2025年面试题趋势也印证了这一点:类加载器、死锁等底层问题被标注为必备知识。结合用户提供的引用材料,需要重点设计三类问题:1.深度原理类:JVM优化(引用[3]强调的分布式环境特性)、并发底层(超越synchronized的表面用法)2.实战设计类:系统设计要包含当前主流技术栈如云原生(引用[1]提到的架构能力)3.软技能类:引用[4]显示技术面最后常问“有什么想问我的”,需准备体现候选人思考深度的问题注意到用户五年经验,要避免基础题(如String不可变性),转而用场景题考察应用能力。例如问“如何用CompletableFuture优化订单流程”就比单纯问线程池参数更有价值。设计答案时需注意:-引用权威资料:如《Java并发实战》体现专业性[^1]-给出落地建议:如Arthas命令直接可用于面试演示-标难度系数:参考引用[5]的星级标注,帮助用户分配准备精力最后生成的“相关问题”要形成进阶路径:从并发编程延伸到分布式事务,再自然过渡到系统设计,符合真实面试流程。</think> 以下是为工作五年的Java工程师设计的面试题预测,结合2024年就业环境(强调云原生、并发、深度调优能力),包含核心问题和简要解析: --- ### 一、JVM深度调优(大厂必考)[^3][^5] **问题1:如何定位和解决频繁Full GC?** **答**: 1. **定位**:`jstat -gcutil`观察内存回收情况 + `jmap -histo:live`分析对象分布 2. **常见原因**: - 内存泄漏:用`MAT`分析堆转储文件,检查未释放的对象引用 - 大对象分配:`-XX:+PrintGCDetails`日志中关注`Humongous Allocation` 3. **优化**:调整`-XX:MaxTenuringThreshold`降低晋升年龄 + `G1`分区大小优化 **问题2:线上OOM如何紧急恢复和根因分析?** **答**: ```bash # 紧急恢复 kill -3 <PID> # 生成heap dump jcmd <PID> GC.run # 强制Full GC # 分析工具 Arthas的`memory`命令 + Eclipse MAT分析对象关联路径 ``` --- ### 二、并发编程实战(关注分布式场景)[^1][^5] **问题3:如何设计一个支持10万QPS的订单系统线程池?** **答**: ```java ThreadPoolExecutor executor = new ThreadPoolExecutor( 50, // 核心线程数 (CPU核数*2) 200, // 最大线程数 (突发流量缓冲) 60s, // 空闲回收 new LinkedBlockingDeque(1000), // 有界队列防OOM new ThreadFactoryBuilder().setNameFormat("order-pool-%d").build(), new CallerRunsPolicy() // 降级策略 ); ``` **关键点**: - 监控线程池状态:`getActiveCount()`+`getQueue().size()` - 配合Hystrix实现熔断 **问题4:Redis分布式锁在超并发下可能遇到什么问题?如何解决?** **答**: - **问题**:锁续期失败、集群脑裂导致锁失效 - **方案**: 1. Redisson的`watchDog`机制自动续期 2. 用`RedLock`算法(多实例加锁) 3. 最终方案:改用`Zookeeper`临时有序节点 --- ### 三、云原生架构(2024重点)[^1][^4] **问题5:K8s中如何保证Spring Boot应用的可用?** **答**: ```yaml livenessProbe: # 存活探针 httpGet: path: /actuator/health port: 8080 readinessProbe: # 就绪探针 tcpSocket: port: 8080 ``` **关键配置**: - HPA自动扩缩容 + `resource.limits`限制资源 - 日志收集:EFK栈对接`logback` **问题6:如何实现Spring Cloud Gateway的动态路由更新?** **答**: 1. 集成`Nacos`配置中心监听路由变更 2. 自定义`RouteDefinitionRepository`实现 3. 配合`RefreshScope`实现热更新 --- ### 四、系统设计(五年经验分水岭) **问题7:设计一个秒杀系统,如何解决超卖问题?** **答**: ```mermaid graph TD A[网关限流] --> B[Redis扣减库存(Lua脚本保证原子性)] B --> C[Kafka异步下单] C --> D[DB最终扣库存] ``` **核心要点**: - Redis集群分片抗压 + 库存预扣减 - 队列削峰 + 数据库乐观锁 --- ### 五、诊断能力(故障排查)[^5] **问题8:CPU突然飙升500%如何快速定位?** **答**: ```bash top -Hp <PID> # 定位占用线程 printf "%x\n" 线程ID # 转16进制 jstack <PID> | grep 16进制ID -A 30 # 查看线程栈 ``` **常见原因**: - 死循环代码 - 锁竞争(`BLOCKED`状态线程) --- ###
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值