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

简介

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

查看沉闷线程数

在Linux上,通过top -H -p 1命令,能够查看java过程的线程状况,其中1是java过程号,如下:

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

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

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

这样能通过top失去线程池的线程数量了,但如何理解线程池的应用状况,即沉闷线程有多少个呢?

通过查看man文档,我发现top命令有一个-i选项,形容如下:

意思就是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


能够看到,20个线程的线程池中,在1秒内只有4个线程是沉闷的,线程池中线程数量是足够的。

这个命令脚本就不开展解释了,也不简单,有linux命令根底的将命令顺次拆开执行,应该能Get到脚本逻辑,没学过linux命令的话,就间接拿去用吧

查看沉闷连接数

在Linux上,应用ss -natp|grep pid=1能够查看1号过程的TCP连贯,如下:

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

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

可见以后有20个redis网络连接,那同样的,其中有多少个是沉闷的呢?

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

能够发现,增加-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


下面其实就是通过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来说,值得好好钻研钻研