共计 3675 个字符,预计需要花费 10 分钟才能阅读完成。
原创:扣钉日记(微信公众号 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 来说,值得好好钻研钻研👍👍👍