首页 > 其他分享 >线上非业务问题排查

线上非业务问题排查

时间:2023-06-25 17:11:10浏览次数:49  
标签:猜想 创建 代码 业务 排查 线程 数过 JVM 线上

  常见的线上问题基本都是业务代码导致的问题,例如某个空指针或者是代码编写存在漏洞。这里记录一下网上看到的容器服务线程数飙升导致的问题

  

一、监控数据

首先看下监控

公司采用Prometheus监控,有较为完善的监控指标,因运维同学说的是线程数过多,那就只列出和线程相关的监控,即存活线程数、RUNNABLE线程数、WAITTING线程数

2.1 存活线程数监控图

存活线程数监控图.png

2.2 RUNNABLE线程数监控图

RUNNABLE线程数监控图.png

2.3 WAITTING线程数监控图

WATTING线程数监控图.png

2.4 7天时间跨度图

7天时间跨度线程数.png

从以上数据可以看到,JVM线程的数量确实在不断的增加,而且大部分都处于WATTING状态


二、猜想

如运维同学所说,JVM的线程数确实很多,那么导致线程数过多的原因有哪些?可以先头脑风暴一下~

  1. 此时服务QPS比较高,导致JVM创建了过多的线程数来处理请求(特别是没有使用线程池,而是直接使用new Thread()构造方法来创建线程的情况)
  2. 服务里某个线程池设置的corePoolSize过于庞大
  3. 服务里创建了过多的线程池

对于猜想2,需要了解下Java线程池提交任务的机制,如下代码所示:

 
public void execute(Runnable command) {
    ...为了更清晰,这里省略了一些代码...
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
 }

 

Java线程池在提交任务时,若线程池中的线程数小于corePoolSize的时候,就会不断地创建新线程来执行任务

对于猜想2和猜想3类似,本质上都是核心线程数设置过多,只不过猜想3是靠线程池的数量堆积起来的核心线程数过多


四、验证

4.1 验证猜想一:服务QPS比较高,导致JVM创建了过多的线程数来处理请求

微服务都有相关的调用量监控,由监控可知,在该时间段内QPS并没有多大的波动,因此可以排除猜想一

4.2 验证猜想二 & 猜想三

之所以将猜想二和猜想三放在一起,是因为通过一个工具即可验证,那就是Arthas

Arthas是阿里提供的Java应用诊断利器,其集成了许多的功能,方便实用

通过Arthas的thread命令可以查看到当前JVM所有的线程。(注意:执行该命令前记得把节点的流量摘掉,以防止对线上业务造成影响)

如下,该命令输出以下数据,我们重点关注NAME数据,即线程名称

IDNAMEGROUPPRIORITYSTATE%CPUTIMEINTERRUPTEDDAEMON
线程ID 线程名称 线程的分类 线程的优先级 线程的状态 线程所占的CPU -- 是否被打断 是否为守护线程

为什么关注NAME数据,这里需要了解下Java线程池创建线程时的命名规则:

在创建线程池时,有个ThreadFactory参数,其为线程创建的工厂,可以在里面指定线程创建时的命令规则,如果不传的话,默认采用的是 java.util.concurrent.Executors.DefaultThreadFactory#DefaultThreadFactory

默认的线程工厂创建线程时的命名规则代码如下:

    DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();

            // 此处是核心
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

 

可以看到,线程名称的命名规则是:pool-poolNumber-thread-threadNum,解释如下

线程命名规则.png

根据此名称的规则可知,若poolNumber数过多,则可证明是线程池数量过多导致的线程数过多。

若threadNum过大,则可证明是某线程池内的线程数过多

在机器上执行 arthas thread后的数据如下图,可知是线程池创建过多导致的JVM线程数过多 arthas的thread命令图.png


五、寻找问题源

在原因确定之后,下面需要确定问题代码在什么地方?

有个思路是我们拿到线程的堆栈,从堆栈里面得知线程执行的业务代码,再根据业务线代码的类以及行数就可以知道线程池创建的地方

Arthas同样提供了查看线程堆栈的功能,很遗憾,在里面没有业务代码。如下图:

arthas线程图.png

于是只能换种思路,根据线程池的创建方式,全局搜索线程池创建处的代码。

线程池的创建一般有三种方式:

  1. 利用JDK自带的工厂类Executors,如:Executors.newFixedThreadPool(1);

  2. 利用线程池的构造函数:如:

 
   private static ThreadPoolExecutor pool = new ThreadPoolExecutor(corePoolSize,
            maximumPoolSize,
            keepAliveTime,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<Runnable>(queueSize),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy());

 

  1. 利用三方工具类创建的线程池,如Guava的MoreExecutors

通过搜索,发现了问题代码:

 
     public void method() {
             // 。。。此处省略了一些代码
            final ExecutorService executorService = Executors.newFixedThreadPool(30);
            for(SubTask sub : mutiTaskReult.getSubTasks()){
                executorService.submit(new ExportAccountFlowRunnable(mutiTaskReult.getMainTask(), sub, logStr));
            }
            // 。。。此处省略了一些代码
        }

 

可见,代码里在方法级的作用域里创建了线程池,从而导致JVM线程数不断地增加。

大家见到这里可能有疑问:这个线程池在方法执行完成后便没有引用了,为什么没有被回收?线程为什么没有被释放?

这个答案可以在ThreadPoolExecutor的注释里得到答案,即:

Finalization A pool that is no longer referenced in a program AND has no remaining threads will be shutdown automatically. If you would like to ensure that unreferenced pools are reclaimed even if users forget to call shutdown, then you must arrange that unused threads eventually die, by setting appropriate keep-alive times, using a lower bound of zero core threads and/or setting allowCoreThreadTimeOut(boolean).

线程池如果不被引用,且没有剩余线程的时候才会被自动关闭。

如果想在没有调用shutdown的时候,线程池也会被关闭回收,那么你必须要保证线程池里面的线程最终都要“死”掉。可以如下的两种方式来设置:

  1. corePoolSize设置为0,且设置一个合适的keep-alive时间

  2. allowCoreThreadTimeOut(boolean) 设置为true,允许核心线程也会被超时回收

我们再往深处想一想,线程池没有被回收的原因只能是被GC ROOTS TRACING了,那么引用线程池的GC ROOTS是什么?

结合MAT对内存的分析,可以发现作为GC ROOTS的Threadtarget属性持有了Worker的引用,而Worker作为内部类同样持有了ThreadPoolExecutor的引用,于是形成了Thread->Worker->ThreadPoolExecutor这样一条隐蔽的关系,具体如下图所示

引用图.png

MAT分析的数据如下所示:

  1. thread的target属性持有了worker的引用

thread引用worker.png

  1. worker的this属性持有了ThreadPoolExecutor的引用

worker引用ThreadPoolExecutor.png


六、总结

本文阐述了一个由JVM线程数过多的问题引起的思考、分析与解决的过程。通过利用Arthas、MAT以及对线程池源码的阅读来达到解决问题的目的

 

标签:猜想,创建,代码,业务,排查,线程,数过,JVM,线上
From: https://www.cnblogs.com/mzBlogs/p/17503388.html

相关文章

  • RTSP/Onvif协议安防平台EasyNVR调用接口录像会被自动删除的原因排查与解决
    EasyNVR安防视频云服务是基于RTSP/Onvif协议接入的视频平台,可支持将接入的视频流进行全平台、全终端的分发,分发的视频流包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等。平台丰富灵活的视频能力,可应用在智慧校园、智慧工厂、智慧水利等场景中。有用户反馈,在使用EasyNVR接入设备......
  • 线上故障的正确打开方式
    对技术同学来说,线上故障是一个绕不开的话题。一方面,线上故障会极大的影响个人的绩效和心态;另一方面,处理线上故障也是很好的提升解决问题能力的机会。因为线上故障的原因是多种多样的,会逼迫你去收集信息,从各种角度分析定位根因,然后想办法去优化解决。处理线上故障的过程,是一个......
  • 网络故障排查
    网络故障排查:1.网卡工具,服务器有多个网卡并且已经配置好运行当中,你却没记得eth0、eth1、eth2…分别对应的是哪个物理的网卡,此时可以使用如下命令:ethtooleth0此时就会看到eth0对应的物理口一个灯在不停的闪烁2.查看网卡状态ifconfigeth0UP(代表网卡开启状态)RUNNING(代表网卡的......
  • “事后达尔文”—— 游戏业务效果评估方法实践
    作者:vivo互联网数据分析团队LuoYandong、ZhangLingchao本文介绍了互联网业务数据效果评估的几种常见问题及方法,并基于分层抽样的逻辑优化出一套可应用于解决用户不均匀的“事后达尔文"分析法,可适用于无法AB测试或人群不均匀的AB测试等场景下的效果评估中,本文会基于实际应用......
  • 记一次服务器排查漏洞
    记一次服务器排查漏洞最近需要在客户现场部署app后端项目,需要将服务器并过去,但客户扫描后发现我们服务器还有一些信息级别的漏洞,要求我们清空了才能并过去,本来是安排运维来干的,但不知啥原因,leader扔我头上了,还pua我说技术啥都需要知道,所以记录一下几个简单漏洞的解决方案HTTP横......
  • 【转】SpringBoot 线上服务假死,CPU 内存正常
    文章来源:blog.csdn.net/zhangcongyi420/article/details/1311395991、背景开发小伙伴都知道线上服务挂掉,基本都是因为cpu或者内存不足,出现GC频繁OOM之类的情况。本篇文章区别以上的情况给小伙伴们带来不一样的服务挂掉。 2、问题排查老规矩在集群环境中同一个服务......
  • 记一次字符串末尾空白丢失的排查 → MySQL 是会玩的!
    开心一刻今天答应准时回家和老婆一起吃晚饭,但临时有事加了会班,回家晚了点回到家,本以为老婆会很生气,但老婆却立即从厨房端出了热着的饭菜老婆:还没吃饭吧,去洗下,来吃饭吧我洗好,坐下吃饭,内心感动十分;老婆坐旁边深情的看着我老婆:你知道谁最爱你吗我毫不......
  • 智能控制系统控制系统控制系统故障排查:智能控制技术控制系统故障排查方法
    目录《智能控制系统控制系统控制系统故障排查:智能控制技术控制系统故障排查方法》随着智能控制系统的不断发展和应用,智能控制技术在工业、农业、医疗、交通等领域的应用也越来越广泛。但是,由于智能控制系统的复杂性和广泛的应用,系统的稳定性和可靠性也面临着巨大的挑战。因此,智......
  • 线上开发
    在小公司里做售后,兼职开发.开发的内容一般直接上线,学着腾讯的小步快跑,及时做更新.有时候会出现问题,做的新功能和老功能相互影响.出现预期之外的漏洞,需要及时补救.可是同时也在开发着新的任务,或者处理着一些客户的售后疑问,没有办法补救好.匆忙里容易出现更多的错误......
  • Etcd 故障排查
    Etcd故障排查Etcd磁盘空间爆满解决方案etcd默认的空间配额限制为2G,超出空间配额限制就会影响服务,所以需要定期清理设置环境变量ETCD_CA_CERT="/etc/kubernetes/pki/etcd/ca.crt"ETCD_CERT="/etc/kubernetes/pki/etcd/server.crt"ETCD_KEY="/etc/kubernetes/pki/etcd/serve......