5

在Linux上查看活跃线程数与连接数 - 扣钉日记

 1 year ago
source link: https://www.cnblogs.com/codelogs/p/17178675.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,非公众号转载保留此声明。

简介#

现如今,有两种常见的软件资源几乎成了Java后端程序的标配,即线程池与连接池,但这些池化资源非常的重要,一旦不够用了,就会导致程序阻塞、性能低下,所以有时我们需要看看它们的使用情况,以判断这里是否是瓶颈。

查看活跃线程数#

在Linux上,通过top -H -p 1命令,可以查看java进程的线程情况,其中1是java进程号,如下:

image_2023-03-04_20230304140014


如上,可以看到线程的名称、CPU使用率等,其中http-nio-8080-e就是Tomcat线程池中的线程,tomcat线程全名类似于http-nio-8080-exec-20,由于Linux中线程名称有长度限制,所以被截断了。

注:jdk8的话,需要jdk8u222以上版本,才能在top中看到线程名称。

我们数一下http-nio-8080-e线程的数量,发现它有20个,正好对应上了在springboot中的线程配置。

image_2023-03-04_20230304133328

这样能通过top得到线程池的线程数量了,但如何了解线程池的使用情况,即活跃线程有多少个呢?

经过查看man文档,我发现top命令有一个-i选项,描述如下:

image_2023-03-04_20230304134557


意思就是i是一个开关选项,默认会显示全部线程,而打开此选项之后,就只显示活跃线程了!

所以,只需要利用-i选项,再配合sed/awk/uniq等文本处理命令,即可以统计出活跃线程数了,如下:

$ top -H -i -b -d 1 -n2 -p 1 | awk -v RS= 'END{print $0}' | awk '$1 ~ /[0-9]+/{print $12}' | sed -E 's/[0-9]+/n/g' | sort | uniq -c

image_2023-03-04_20230304141731


可以看到,20个线程的线程池中,在1秒内只有4个线程是活跃的,线程池中线程数量是足够的。

这个命令脚本就不展开解释了,也不复杂,有linux命令基础的将命令依次拆开执行,应该能Get到脚本逻辑,没学过linux命令的话,就直接拿去用吧😅

查看活跃连接数#

在Linux上,使用ss -natp|grep pid=1可以查看1号进程的TCP连接,如下:

image_2023-03-04_20230304144050


比如若redis数据库端口是6379的话,那么可这样查看redis连接池中连接数量,如下:

$ ss -natp | grep pid=1 | awk '$5~/:6379$/' | wc -l
20

可见当前有20个redis网络连接,那同样的,其中有多少个是活跃的呢?

经过查看man文档,发现ss中也有一个-i选项,如下:

image_2023-03-04_20230304145652


可以发现,添加-i选项后,ss会输出tcp连接中的一些额外信息,其中lastsnd表示最后一次发送包到当前所经历的毫秒数,lastrcv表示最后一次接收包到当前所经历的毫秒数。

有了这个信息后,就可以通过awk过滤出lastsnd或lastrcv小于1000的tcp连接,这些连接即是1秒内活跃过的连接了,因此我又编写了如下命令脚本。

$ ss -natpi | sed '1!{N;s/\n//;}' | grep pid=1 | awk -v t=1000 'match($0,/lastsnd:(\w+) lastrcv:(\w+)/,a) && (a[1]<t || a[2]<t) && match($4,/(.+):(\w+)$/,s) && match($5,/(.+):(\w+)$/,d) && s[2]>=32768{print d[2]}' |sort |uniq -c |sort -nk2
      8 80
      3 3306
      7 3307
      6 6379
      1 7916

如上,可以看到各连出端口的活跃连接情况,其中80是http连接池端口,3306与3307是MySQL主从库的连接池端口,6379是redis连接池的端口。

这是java应用主动连出连接的活跃情况,那调用方连入java应用的呢?

其实只需要稍微调整一下awk脚本即可,如下:

  1. s[2]>=32768调整为s[2]<32768,其中32768是Linux默认的临时端口号的分界线,可通过sysctl net.ipv4.ip_local_port_range查询,本地端口号大于这个值,代表是连出连接.
  2. print d[2]调整为print s[2],和上面条件联合起来,输出的就是本地监听端口了.

调整后,效果如下:

$ ss -natpi | sed '1!{N;s/\n//;}' | grep pid=1 | awk -v t=1000 'match($0,/lastsnd:(\w+) lastrcv:(\w+)/,a) && (a[1]<t || a[2]<t) && match($4,/(.+):(\w+)$/,s) && match($5,/(.+):(\w+)$/,d) && s[2]<32768{print s[2]}' |sort |uniq -c |sort -nk2
     8 8080

可以发现,我们服务的8080端口,1秒内活跃过的连接数是8个。

注:只有当调用方也使用连接池时,这种方法获取到的活跃连接数才是准确的,若调用方使用短链接的话,则不准确。

arthas查看活跃线程数与连接数#

通过上面的方法,已经可以查看活跃线程数与连接数了,但有些情况下,会丧失一些细节,如下:

  1. top中的线程名会截断,如果不同线程池的线程名前16字符一样,则在top中无法区分。
  2. ss中是通过端口来区分线程池的,但http服务的端口号基本都是80或443,所以不同域名的http服务的连接池无法区分。

若需要分辩这些细节,还是要深入到jvm里面来,而arthas就是一个不错的工具,它的vmtool命令能够获取指定类型的Java对象,并从Java对象中获取信息。

以springboot为例,获取内置tomcat线程池的活跃情况,如下:

# --action getInstances:表示获取对象实例
# --classLoaderClass:指定类加载器
# --className:指定要获取哪个类的实例
# --express:指定ognl表达式,用来从对象上获取信息
[arthas@1]$ vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.apache.tomcat.util.threads.ThreadPoolExecutor  --express 'instances.{ #{"ActiveCount":getActiveCount(),"LargestPoolSize":getLargestPoolSize(),"CorePoolSize":getCorePoolSize(),"MaximumPoolSize":getMaximumPoolSize(),"QueueSize":getQueue().size(),"ThreadName":getThreadFactory().namePrefix }}' -x 2

image_2023-03-04_20230304155857


上面其实就是通过vmtool工具,获取到了tomcat的线程池对象,然后调用线程池的getActiveCount()等方法,获取到了活跃线程数🙄

要获取连接池的活跃情况,也一并呈上吧,如下:

# 获取druid连接池的使用情况
[arthas@1]$ vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className com.alibaba.druid.pool.DruidDataSource  --express 'instances.{ #{"url":#this.getUrl().split("\\?")[0], "username":#this.getUsername(),"PoolingCount":#this.getPoolingCount(),"ActiveCount":#this.getActiveCount(),"MaxActive":#this.getMaxActive(),"WaitThreadCount":#this.getWaitThreadCount(),"MaxWaitThreadCount":#this.getMaxWaitThreadCount()} }' -x 2

# 获取httpclient连接池的使用情况
[arthas@1]$ vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.apache.http.impl.conn.PoolingHttpClientConnectionManager --express 'instances.{ #pool=#this.pool.routeToPool.values() }' -x2

可以看到,arthas真的很方便实用,对于Java Boy来说,值得好好研究研究👍👍👍


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK