记一次Nacos线程数飙升排查

这篇具有很好参考价值的文章主要介绍了记一次Nacos线程数飙升排查。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

近日有个项目用到了Nacos做注册中心。运行一段时间发现Nacos服务的线程数达到了1k+。这肯定是不正常的。

环境:
  • 镜像nacos-server 2.2.3
  • docker-compose编排部署
  • Nacos standalone模式
  nacos:
    image: "nacos/nacos-server:latest"
    environment:
      - JAVA_OPTS=-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -Xms1024m -Xmx1024m -Xss256k -XX:SurvivorRatio=8 -XX:+UseG1GC -Dremote.executor.times.of.processors=1 
      - MODE=standalone
      - NACOS_COMMON_PROCESSORS=2
    container_name: nacos
    hostname: nacos
    restart: always
    volumes:
      - ./nacos/logs:/home/nacos/logs
      - ./nacos/conf/application.properties:/home/nacos/conf/application.properties
      - ./nacos/data:/home/nacos/data
    networks:
      - xxxx
    ports:
      - "8848:8848"
      - "9848:9848"
问题表现

docker stats nacos 发现该容器的线程数1k+
用Fastthread分析stack文件表现如下
记一次Nacos线程数飙升排查,Spring Cloud Alibaba,Nacos,线程,docker,spring boot
记一次Nacos线程数飙升排查,Spring Cloud Alibaba,Nacos,线程,docker,spring boot
数量最多的线程线程栈如下
记一次Nacos线程数飙升排查,Spring Cloud Alibaba,Nacos,线程,docker,spring boot
数量最多的nacos-grpc-executor线程达到五百多条,并且都处于WATING状态。线程栈并看不出来有业务代码。可以看出来是某个线程池创建的核心线程没有回收。在等待新任务到来。线程在线程池中不断的循环获取任务,因为获取不到任务所以进入了waiting状态,等待着有任务后被唤醒。
查看Nacos-server的源码发现,在com.alibaba.nacos.core.utils.GlobalExecutor中找到了这个线程池
记一次Nacos线程数飙升排查,Spring Cloud Alibaba,Nacos,线程,docker,spring boot
并且核心线程和最大线程设置为一样的,也没有开启核心线程回收

查看RemoteUtils.getRemoteExecutorTimesOfProcessors()方法以及EnvUtil.getAvailableProcessors

    /**
     * get remote executors thread times of processors,default is 64. see the usage of this method for detail.
     *
     * @return times of processors.
     */
    public static int getRemoteExecutorTimesOfProcessors() {
        String timesString = System.getProperty("remote.executor.times.of.processors");
        if (NumberUtils.isDigits(timesString)) {
            int times = Integer.parseInt(timesString);
            return times > 0 ? times : REMOTE_EXECUTOR_TIMES_OF_PROCESSORS;
        } else {
            return REMOTE_EXECUTOR_TIMES_OF_PROCESSORS;
        }
    }
    
    public static int getAvailableProcessors(int multiple) {
        if (multiple < 1) {
            throw new IllegalArgumentException("processors multiple must upper than 1");
        }
        Integer processor = getProperty(Constants.AVAILABLE_PROCESSORS_BASIC, Integer.class);
        return null != processor && processor > 0 ? processor * multiple : ThreadUtils.getSuitableThreadCount(multiple);
    }

该线程池核心线程数量的计算方法 由参数remote.executor.times.of.processors和Constants.AVAILABLE_PROCESSORS_BASIC控制,取二者乘积。若没有设置该参数,取当前可用核心数量作为核心线程数量。
在服务启动时添加JVM启动参数设置remote.executor.times.of.processors的数量,并把nacos.core.sys.basic.processors参数添加到Nacos的applical.properties配置文件中。即可很好的控制该线程池的线程数量。
此外在ThreadUtils.getSuitableThreadCount方法是控制默认可用线程数量的

    public static int getSuitableThreadCount(int threadMultiple) {
        final int coreCount = PropertyUtils.getProcessorsCount();
        int workerCount = 1;
        while (workerCount < coreCount * threadMultiple) {
            workerCount <<= 1;
        }
        return workerCount;
    }
    private static final String PROCESSORS_ENV_NAME = "NACOS_COMMON_PROCESSORS";
    
    private static final String PROCESSORS_PROP_NAME = "nacos.common.processors";
    
    public static int getProcessorsCount() {
        int processorsCount = 0;
        String processorsCountPreSet = getProperty(PROCESSORS_PROP_NAME, PROCESSORS_ENV_NAME);
        if (processorsCountPreSet != null) {
            try {
                processorsCount = Integer.parseInt(processorsCountPreSet);
            } catch (NumberFormatException ignored) {
            }
        }
        if (processorsCount <= 0) {
            processorsCount = Runtime.getRuntime().availableProcessors();
        }
        return processorsCount;
    }

在配置文件applical.properties中添加nacos.common.processors参数即可。
重启Nacos服务后线程数量趋于正常。


额外补充

一个对象能不能回收,是看它到gc root之间有没有可达路径,线程池不能回收说明到达线程池的gc root还是有可达路径的。这里讲个冷知识,这里的线程池的gc root是线程,具体的gc路径是thread->workers->线程池。

线程对象是线程池的gc root,假如线程对象能被gc,那么线程池对象肯定也能被gc掉(因为线程池对象已经没有到gc root的可达路径了)。

一条正在运行的线程是gc root,注意,是正在运行,这个正在运行我先透露下,即使是waiting状态,也算正在运行。这个回答的整体的意思是,运行的线程是gc root,但是非运行的线程不是gc root(可以被回收)

线程池和线程被回收的关键就在于线程能不能被回收,那么回到原来的起点,为何调用线程池的shutdown方法能够导致线程和线程池被回收呢?难道是shutdown方法把线程变成了非运行状态吗?

public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(SHUTDOWN);
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
}

private void interruptIdleWorkers() {
        interruptIdleWorkers(false);
}

private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers) {
                Thread t = w.thread;
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
}

看到interruptIdleWorkers方法,这个方法里面主要就做了一件事,遍历当前线程池中的线程,并且调用线程的interrupt()方法,通知线程中断,也就是说shutdown方法只是去遍历所有线程池中的线程,然后通知线程中断。
在worker对象的runwoker方法的gettask()方法会调用poll方法或take方法从工作队列中取任务
poll方法和take方法都会让当前线程进入time_waiting或者waiting状态。而当线程处于在等待状态的时候,我们调用线程的interrupt方法,毫无疑问会使线程当场抛出中断异常
也就是说线程池的shutdownnow方法调用interruptIdleWorkers去对线程对象interrupt是为了让处于waiting或者是time_waiting的线程抛出异常。

总结shutdownnow方法
  • 线程池调用shutdownnow方法是为了调用worker对象的interrupt方法,来打断那些沉睡中的线程(waiting或者time_waiting状态),使其抛出异常
  • 线程池会把抛出异常的worker对象从workers集合中移除引用,此时被移除的worker对象因为没有到达gc root的路径已经可以被gc掉了
  • 等到workers对象空了,并且当前tomcat线程也结束,此时线程池对象也可以被gc掉,整个线程池对象成功释放

记一次Nacos线程数飙升排查,Spring Cloud Alibaba,Nacos,线程,docker,spring boot文章来源地址https://www.toymoban.com/news/detail-704909.html

到了这里,关于记一次Nacos线程数飙升排查的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Spring Cloud Alibaba--Nacos服务注册和配置中心

    Spring Cloud Alibaba--Nacos服务注册和配置中心

    Nacos 是Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos的特性,官网解释如下: 服务发现和服务健康监测 Nacos 支持基于 DNS 和基于 RPC 的服务发现。服务提供者使用 原生SDK、OpenAPI、或一个独立的Age

    2024年02月05日
    浏览(41)
  • Spring Cloud Alibaba Nacos(服务注册及配置中心)

    Nacos是由阿里巴巴提供的一款专门构建云本地应用的 动态服务发现、配置中心和服务管理平台 。 在Spring Cloud Alibaba中常使用Nacos作为注册中心和分布式配置中心。  (1)服务发现和管理 动态服务发现是以服务为中心(例如微服务或云原生)体系结构的关键。Nacos支持基于DN

    2024年02月16日
    浏览(51)
  • 【微服务 Spring Cloud Alibaba】- Nacos 服务注册中心

    【微服务 Spring Cloud Alibaba】- Nacos 服务注册中心

    目录 1. 什么是注册中心? 1.2 注册中心的作用 2. SpringBoot 整合 Nacos 实现服务注册中心 2.1 将服务注册到 Nacos 2.2 实现消费者 3. 服务列表各个参数的含义、作用以及应用场景 注册中心是微服务架构中的一个重要组件,它用于实现 服务注册 与 服务发现 。 【思考一】什么叫服务

    2024年02月06日
    浏览(49)
  • 云原生微服务实战 Spring Cloud Alibaba 之 Nacos

    云原生微服务实战 Spring Cloud Alibaba 之 Nacos

    第一章 Java线程池技术应用 第二章 CountDownLatch和Semaphone的应用 第三章 Spring Cloud 简介 第四章 Spring Cloud Netflix 之 Eureka 第五章 Spring Cloud Netflix 之 Ribbon 第六章 Spring Cloud 之 OpenFeign 第七章 Spring Cloud 之 GateWay 第八章 Spring Cloud Netflix 之 Hystrix 第九章 代码管理gitlab 使用 第十章 Spr

    2024年02月08日
    浏览(46)
  • Spring Cloud Alibaba 2021.0.1.0之Nacos升级

    1 背景 本次隆重发布的 Spring Cloud Alibaba 2021.0.1.0 版本在 Spring Cloud 2021.0.1、Spring Boot 2.6.3 的基础上对其中包括注册配置中心、分布式消息等在内的众多组件进行重大升级: Nacos :升级 Nacos 客户端到 1.4.2 版本,修复了 Nacos 1.4.1 所存在的相关问题,支持了 Nacos 服务发现失败容错

    2024年02月08日
    浏览(12)
  • 【Spring Cloud Alibaba】2.服务注册与发现(Nacos安装)

    【Spring Cloud Alibaba】2.服务注册与发现(Nacos安装)

    我们要搭建一个 Spring Cloud Alibaba 项目就绕不开 Nacos ,阿里巴巴提供的 Nacos 组件,可以提供服务注册与发现和分布式配置服务,拥有着淘宝双十一十几年的流量经验,还是非常的可靠的。 Nacos 依赖 Java 环境来运行。如果您是从代码开始构建并运行Nacos,还需要为此配置 Maven环

    2024年01月23日
    浏览(13)
  • 【springcloud 微服务】Spring Cloud Alibaba Nacos使用详解

    目录 一、前言 二、nacos介绍 2.1  什么是 Nacos 2.2 nacos 核心能力 2.2.1 服务发现和服务健康监测

    2024年01月22日
    浏览(14)
  • 【Spring Cloud Alibaba】Nacos config的使用和高阶用法

    【Spring Cloud Alibaba】Nacos config的使用和高阶用法

    Nacos 提供用于存储配置和其他元数据的 key/value 存储,为分布式系统中的外部化配置提供服务器端和客户端支持。使用 Spring Cloud Alibaba Nacos Config,您可以在 Nacos Server 集中管理你 Spring Cloud 应用的外部属性配置。 Spring Cloud Alibaba Nacos Config 是 Config Server 和 Client 的替代方案,客

    2024年02月06日
    浏览(8)
  • day10-Spring Cloud Alibaba Nacos-服务注册与配置中心

    官网:https://github.com/alibaba/nacos,Nacos官方中文手册 Nacos:Spring Cloud Alibaba服务注册与配置中心(非常详细) (biancheng.net) Spring Cloud Alibaba系列-一文读懂Nacos原理 - 掘金 (juejin.cn) Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服

    2024年02月08日
    浏览(67)
  • Spring Cloud Alibaba-02-Nacos Discovery服务治理及负载均衡

    Spring Cloud Alibaba-02-Nacos Discovery服务治理及负载均衡

    Lison dreamlison@163.com , v1.0.0 , 2023.05.10 先来思考一个问题 通过上一章的操作,我们已经可以实现微服务之间的调用。但是我们把服务提供者的网络地址 (ip,端口)等硬编码到了代码中,这种做法存在许多问题: 一旦服务提供者地址变化,就需要手工修改代码 一旦是多个服务提供

    2024年02月22日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包