SpringCloud Gateway 动态路由配置导致cpu满载,内存耗完
SpringCloud Gateway导致cpu满载,内存耗完问题发现问题查找问题分析动态配置代码问题发现**程序打好包丢上测试环境之后,正常运行了一段时间,可是一个周末回来,发现服务器卡得不行,一开始以为是机器问题,指定堆栈大小重启然后内存再给大一点,结果过了几天还是一样,于是开始怀疑程序问题了。**问题查找ps aux|head -1;ps aux|grep -v PID|sort -rn -
SpringCloud Gateway导致cpu满载,内存耗完
问题发现
**
程序打好包丢上测试环境之后,正常运行了一段时间,可是一个周末回来,发现服务器卡得不行,一开始以为是机器问题,指定堆栈大小重启然后内存再给大一点,结果过了几天还是一样,于是开始怀疑程序问题了。
**
问题查找
ps aux|head -1;ps aux|grep -v PID|sort -rn -k +3|head
查询服务器的占用情况,发现springcloud gateway程序cpu占用率99%,内存300+%。
问题分析
这个明显是一个内存溢出的问题,查询了一下issue和搜索一下相关问题,并没有相似的情况,只能自己着手分析了。
// 查询最高占用的进程id
top -Hp 6666
//看下线程的内容
jstack 7777
查询一下线程的的信息,发现并没有占用特别高的线程,用jstack看了下也没有发现奇怪的地方,但是还是得重新看下。看着看着一个 Listener 的单词引起了我的注意,想起了代码中的某一个部分。
我的网关代码分两类,一类是filter,一类是动态路由配置。动态路由配置使用到了监听器。
public class NacosRouteDefinitionRepository implements RouteDefinitionLocator {
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
try {
String content = NacosFactory.createConfigService(nacosConfigProperties.assembleConfigServiceProperties()).getConfig(DATA_ID, GROUP_ID,5000);
List<RouteDefinition> routeDefinitions = getListByYAML(content);
return Flux.fromIterable(routeDefinitions);
} catch (NacosException e) {
log.error("getRouteDefinitions by nacos error", e);
}
return Flux.fromIterable(new ArrayList());
}
/**
* 添加Nacos监听
*/
private void addListener() {
try {
NacosFactory.createConfigService(nacosConfigProperties.assembleConfigServiceProperties()).addListener(DATA_ID, GROUP_ID, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
publisher.publishEvent(new RefreshRoutesEvent(this));
}
});
} catch (NacosException e) {
log.error("nacos-addListener-error", e);
}
}
}
我立马禁用了动态路由配置重新启动,然后继续分析。
经过一轮的debug之后,发现 getRouteDefinitions() 这个方法是每隔几秒钟就会执行一次,这时候终于发现问题所在了。
String content = NacosFactory.createConfigService(nacosConfigProperties.assembleConfigServiceProperties())
.getConfig(DATA_ID, GROUP_ID,5000);
这个 NacosFactory.createConfigService() 是会创建一个ConfigService对象的,这段代码相当于每隔几秒钟就创建了一个对象,这个对象gc比较难回收,所以就产生内存爆了,内存爆了cpu也跟着爆了。
动态配置代码
最后给个动态路由配置代码,也是参考了各路文章。
配置文件: application.yml
#动态更新路由开关
gateway:
dynamicRoute:
enabled: true
dataId: dynamic-routes
groupId: TEST_GROUP
配置类: DynamicRouteConfig.class
@Configuration
@ConditionalOnProperty(prefix = "gateway.dynamicRoute", name = "enabled", havingValue = "true")
public class DynamicRouteConfig {
@Autowired
private ApplicationEventPublisher publisher;
/**
* Nacos实现方式
*/
@Configuration
public class NacosDynRoute {
@Value("${gateway.dynamicRoute.dataId}")
private String dataId;
@Value("${gateway.dynamicRoute.groupId}")
private String groupId ;
@Autowired
private NacosConfigManager nacosConfigManager;
@Bean
public NacosRouteDefinitionRepository nacosRouteDefinitionRepository() {
return new NacosRouteDefinitionRepository(publisher, nacosConfigManager,dataId,groupId);
}
}
}
NacosRouteDefinitionRepository.class
@Slf4j
public class NacosRouteDefinitionRepository implements RouteDefinitionLocator {
private String dataId ;
private String groupId ;
/**
* RouteDefinitionLocator里面的getRouteDefinitions方法是每隔几秒钟就会执行一次的。
* 配置修改的情况比较少,等监听到有修改的时候再做更新。
*/
private static boolean IS_UPDATE = false;
private List<RouteDefinition> routeDefinitions;
private ApplicationEventPublisher publisher;
private NacosConfigManager nacosConfigManager;
public NacosRouteDefinitionRepository(ApplicationEventPublisher publisher, NacosConfigManager nacosConfigManager, String dataId, String groupId) {
this.dataId = dataId;
this.groupId = groupId;
this.publisher = publisher;
this.nacosConfigManager = nacosConfigManager;
addListener();
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
try {
if (routeDefinitions == null || IS_UPDATE){
String content = nacosConfigManager.getConfigService().getConfig(dataId, groupId,5000);
routeDefinitions = getListByYAML(content);
IS_UPDATE = false;
}
} catch (NacosException e) {
log.error("getRouteDefinitions by nacos error", e);
}
return Flux.fromIterable(routeDefinitions);
}
/**
* 添加Nacos监听
*/
private void addListener() {
try {
nacosConfigManager.getConfigService().addListener(dataId, groupId, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
IS_UPDATE = true;
publisher.publishEvent(new RefreshRoutesEvent(this));
}
});
} catch (NacosException e) {
log.error("nacos-addListener-error", e);
}
}
/**
* json方式
* @param content
* @return
*/
private List<RouteDefinition> getListByStr(String content) {
if (StringUtils.isNotEmpty(content)) {
return JSONObject.parseArray(content, RouteDefinition.class);
}
return new ArrayList<>(0);
}
/**
* yml方式
* @param content
* @return
*/
private List<RouteDefinition> getListByYAML(String content) {
if (StringUtils.isNotEmpty(content)) {
return Arrays.asList(new Yaml().loadAs(content, Route.class).getRoutes());
}
return new ArrayList<>(0);
}
更多推荐
所有评论(0)