原创:扣钉日记(微信大众号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 -l
20
可见当前有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脚本即可,如下:
- 将
s[2]>=32768
调整为s[2]<32768
,其间32768是Linux默许的临时端口号的分界线,可经过sysctl net.ipv4.ip_local_port_range
查询,本地端口号大于这个值,代表是连出衔接. - 将
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检查活泼线程数与衔接数
经过上面的办法,已经能够检查活泼线程数与衔接数了,但有些状况下,会丧失一些细节,如下:
- top中的线程名会切断,假如不同线程池的线程名前16字符相同,则在top中无法区别。
- 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来说,值得好好研究研究