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.csdn.net/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
,也很不错,可以试试,欢迎讨论!