nacos1.2.1配置索引出错导致CPU占用过高

问题背景:
网关配置索引出错导致CPU占用过高

排查过程:
首先排查机器CPU占用率,使用top命令
在这里插入图片描述
占用CPU最高的进程为19414进程
查看该进程中占用CPU最高的线程 top -Hp 19414
在这里插入图片描述

将线程id 19492转换为16进制 为4c24
查询占用cpu最高线程的栈 jstack 19414 |grep 4c24 -A 20
在这里插入图片描述

终于定位到哪一个线程出的问题,可以看到该线程是一个nacos工作线程 com.alibaba.nacos.client.Worker.longPolling.fixed-xxxx
在这里插入图片描述

这是一个长轮询线程,如果只是单线程执行肯定不会有问题,问题应该是出现在不停的创建了clientWork对象上
排查这个对象是如果创建

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

这里能够看出来,nacos每次刷新都会触发实例创建,验证想法,通过jamp命令
jmap -histo:live 6853 |grep ClientWorker

果然,有438个work对象,并且随着时间逐渐变大

在这里插入图片描述
事情到这里就比较明确了,只需要排查出,为什么会一直不停的创建ClientWorker,我们把断点打到创建ClientWorker的方法上
在这里插入图片描述
通过堆栈分析,找到创建ClientWorker的代码堆栈
在这里插入图片描述
通过堆栈分析,长轮询获取配置信息时,会定时校验 cacheData.checkListenerMd5()
我们可以看到创建ClientWorker最终源头还是来源于ClientWorker类,以下是调用cacheData.checkListenerMd5()的源码

class LongPollingRunnable implements Runnable {
    private int taskId;

    public LongPollingRunnable(int taskId) {
        this.taskId = taskId;
    }

    @Override
    public void run() {

        List<CacheData> cacheDatas = new ArrayList<CacheData>();
        List<String> inInitializingCacheList = new ArrayList<String>();
        try {
            // check failover config
            for (CacheData cacheData : cacheMap.get().values()) {
                if (cacheData.getTaskId() == taskId) {
                    cacheDatas.add(cacheData);
                    try {
                        checkLocalConfig(cacheData);
                        if (cacheData.isUseLocalConfigInfo()) {
                            cacheData.checkListenerMd5();
                        }
                    } catch (Exception e) {
                        LOGGER.error("get local config info error", e);
                    }
                }
            }

            // check server config
            List<String> changedGroupKeys = checkUpdateDataIds(cacheDatas, inInitializingCacheList);
            LOGGER.info("get changedGroupKeys:" + changedGroupKeys);
            此处省略
            、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
          
            for (CacheData cacheData : cacheDatas) {
                if (!cacheData.isInitializing() || inInitializingCacheList
                    .contains(GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.tenant))) {
                    // 校验本地配置和服务端配置md5
                    cacheData.checkListenerMd5();
                    cacheData.setInitializing(false);
                }
            }
            inInitializingCacheList.clear();

            executorService.execute(this);

        } catch (Throwable e) {

            // If the rotation training task is abnormal, the next execution time of the task will be punished
            LOGGER.error("longPolling error : ", e);
            executorService.schedule(this, taskPenaltyTime, TimeUnit.MILLISECONDS);
        }
    }
}

猜测如果不一致会刷新spring配置容器,应该是这里报错导致
接着分析cacheData.checkListenerMd5()代码

void checkListenerMd5() {
    for (ManagerListenerWrap wrap : listeners) {
        if (!md5.equals(wrap.lastCallMd5)) {
            safeNotifyListener(dataId, group, content, type, md5, wrap);
        }
    }
}

如果服务端和本地md5不一致,则会调用safeNotifyListener监听方法,并执行job.run方法
在这里插入图片描述
在这里插入图片描述
上图是job.run最终报错的代码receiveConfigInfo报错,spring绑定配置信息异常,并且导致213行listenerWrap.lastCallMd5 = md5;没有成功执行到,因此会一直死循环

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值