You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 lines
11 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 35 | 如何监控Tomcat的性能
专栏上一期我们分析了JVM GC的基本原理以及监控和分析工具今天我们接着来聊如何监控Tomcat的各种指标因为只有我们掌握了这些指标和信息才能对Tomcat内部发生的事情一目了然让我们明白系统的瓶颈在哪里进而做出调优的决策。
在今天的文章里我们首先来看看到底都需要监控Tomcat哪些关键指标接着来具体学习如何通过JConsole来监控它们。如果系统没有暴露JMX接口我们还可以通过命令行来查看Tomcat的性能指标。
Web应用的响应时间是我们关注的一个重点最后我们通过一个实战案例来看看Web应用的下游服务响应时间比较长的情况下Tomcat的各项指标是什么样子的。
## Tomcat的关键指标
Tomcat的关键指标有**吞吐量、响应时间、错误数、线程池、CPU以及JVM内存**。
我来简单介绍一下这些指标背后的意义。其中前三个指标是我们最关心的业务指标Tomcat作为服务器就是要能够又快有好地处理请求因此吞吐量要大、响应时间要短并且错误数要少。
而后面三个指标是跟系统资源有关的当某个资源出现瓶颈就会影响前面的业务指标比如线程池中的线程数量不足会影响吞吐量和响应时间但是线程数太多会耗费大量CPU也会影响吞吐量当内存不足时会触发频繁地GC耗费CPU最后也会反映到业务指标上来。
那如何监控这些指标呢Tomcat可以通过JMX将上述指标暴露出来的。JMXJava Management Extensions即Java管理扩展是一个为应用程序、设备、系统等植入监控管理功能的框架。JMX使用管理MBean来监控业务资源这些MBean在JMX MBean服务器上注册代表JVM中运行的应用程序或服务。每个MBean都有一个属性列表。JMX客户端可以连接到MBean Server来读写MBean的属性值。你可以通过下面这张图来理解一下JMX的工作原理
![](https://static001.geekbang.org/resource/image/71/6b/714fa2e12380122599be077c10375a6b.png)
Tomcat定义了一系列MBean来对外暴露系统状态接下来我们来看看如何通过JConsole来监控这些指标。
## 通过JConsole监控Tomcat
首先我们需要开启JMX的远程监听端口具体来说就是设置若干JVM参数。我们可以在Tomcat的bin目录下新建一个名为`setenv.sh`的文件(或者`setenv.bat`,根据你的操作系统类型),然后输入下面的内容:
```
export JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote"
export JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote.port=9001"
export JAVA_OPTS="${JAVA_OPTS} -Djava.rmi.server.hostname=x.x.x.x"
export JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote.ssl=false"
export JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote.authenticate=false"
```
重启Tomcat这样JMX的监听端口9001就开启了接下来通过JConsole来连接这个端口。
```
jconsole x.x.x.x:9001
```
我们可以看到JConsole的主界面
![](https://static001.geekbang.org/resource/image/80/d7/80f14c4bd4eead05f4d84937ed7726d7.png)
前面我提到的需要监控的关键指标有**吞吐量、响应时间、错误数、线程池、CPU以及JVM内存**接下来我们就来看看怎么在JConsole上找到这些指标。
**吞吐量、响应时间、错误数**
在MBeans标签页下选择GlobalRequestProcessor这里有Tomcat请求处理的统计信息。你会看到Tomcat中的各种连接器展开“http-nio-8080”你会看到这个连接器上的统计信息其中maxTime表示最长的响应时间processingTime表示平均响应时间requestCount表示吞吐量errorCount就是错误数。
![](https://static001.geekbang.org/resource/image/ff/9c/ff0a9163fbeeed8eb84ed89c3f71799c.png)
**线程池**
选择“线程”标签页可以看到当前Tomcat进程中有多少线程如下图所示
![](https://static001.geekbang.org/resource/image/78/b8/78858a3264107e1f1a0b6a78d43113b8.png)
图的左下方是线程列表右边是线程的运行栈这些都是非常有用的信息。如果大量线程阻塞通过观察线程栈能看到线程阻塞在哪个函数有可能是I/O等待或者是死锁。
**CPU**
在主界面可以找到CPU使用率指标请注意这里的CPU使用率指的是Tomcat进程占用的CPU不是主机总的CPU使用率。
![](https://static001.geekbang.org/resource/image/1b/9f/1bb88cc3d5f2b9a80377a29b0b80e19f.png)
**JVM内存**
选择“内存”标签页你能看到Tomcat进程的JVM内存使用情况。
![](https://static001.geekbang.org/resource/image/f2/02/f22eca547ca76eb5edba03b39082fa02.png)
你还可以查看JVM各内存区域的使用情况大的层面分堆区和非堆区。堆区里有分为Eden、Survivor和Old。选择“VM Summary”标签可以看到虚拟机内的详细信息。
![](https://static001.geekbang.org/resource/image/cc/a0/cc91bb5b7b8d8b0b46dad946617b01a0.png)
## 命令行查看Tomcat指标
极端情况下如果Web应用占用过多CPU或者内存又或者程序中发生了死锁导致Web应用对外没有响应监控系统上看不到数据这个时候需要我们登陆到目标机器通过命令行来查看各种指标。
1.首先我们通过ps命令找到Tomcat进程拿到进程ID。
![](https://static001.geekbang.org/resource/image/84/9a/8477832ebe079cf92e3cd58766754e9a.png)
2.接着查看进程状态的大致信息,通过`cat/proc/<pid>/status`命令:
![](https://static001.geekbang.org/resource/image/d8/b3/d812ac93be2ac882e689f77e5d8e12b3.png)
3.监控进程的CPU和内存资源使用情况
![](https://static001.geekbang.org/resource/image/6e/d6/6e7e0730e92e3d8846f37ea8a14973d6.png)
4.查看Tomcat的网络连接比如Tomcat在8080端口上监听连接请求通过下面的命令查看连接列表
![](https://static001.geekbang.org/resource/image/14/61/14ba365d585f0ec79543efc1a9b32961.png)
你还可以分别统计处在“已连接”状态和“TIME\_WAIT”状态的连接数
![](https://static001.geekbang.org/resource/image/aa/2c/aaab0fb1156bb8ec92ee6c02b149cf2c.jpg)
5.通过ifstat来查看网络流量大致可以看出Tomcat当前的请求数和负载状况。
![](https://static001.geekbang.org/resource/image/67/b2/67a9a29b8bf071152ccc1bc108adc4b2.png)
## 实战案例
在这个实战案例中我们会创建一个Web应用根据传入的参数latency来休眠相应的秒数目的是模拟当前的Web应用在访问下游服务时遇到的延迟。然后用JMeter来压测这个服务通过JConsole来观察Tomcat的各项指标分析和定位问题。
主要的步骤有:
1.创建一个Spring Boot程序加入下面代码所示的一个RestController
```
@RestController
public class DownStreamLatency {
@RequestMapping("/greeting/latency/{seconds}")
public Greeting greeting(@PathVariable long seconds) {
try {
Thread.sleep(seconds * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Greeting greeting = new Greeting("Hello World!");
return greeting;
}
}
```
从上面的代码我们看到程序会读取URL传过来的seconds参数先休眠相应的秒数再返回请求。这样做的目的是客户端压测工具能够控制服务端的延迟。
为了方便观察Tomcat的线程数跟延迟之间的关系还需要加大Tomcat的最大线程数我们可以在`application.properties`文件中加入这样一行:
```
server.tomcat.max-threads=1000server.tomcat.max-threads=1000
```
2.启动JMeter开始压测这里我们将压测的线程数设置为100
![](https://static001.geekbang.org/resource/image/89/51/895c7c986d0c86b0a869a604c9c6af51.png)
请你注意的是我们还需要将客户端的Timeout设置为1000毫秒这是因为JMeter的测试线程在收到响应之前不会发出下一次请求这就意味我们没法按照固定的吞吐量向服务端加压。而加了Timeout以后JMeter会有固定的吞吐量向Tomcat发送请求。
![](https://static001.geekbang.org/resource/image/3e/a5/3e275c10132680995ac83460f29a1aa5.png)
3.开启测试这里分三个阶段第一个阶段将服务端休眠时间设为2秒然后暂停一段时间。第二和第三阶段分别将休眠时间设置成4秒和6秒。
![](https://static001.geekbang.org/resource/image/8b/0d/8b071d99cbf8875c138f14bd17f0ed0d.png)
4.最后我们通过JConsole来观察结果
![](https://static001.geekbang.org/resource/image/dd/b3/ddb8609042469ee73ddc3fb68f676eb3.png)
下面我们从线程数、内存和CPU这三个指标来分析Tomcat的性能问题。
* 首先看线程数在第一阶段时间之前线程数大概是40第一阶段压测开始后线程数增长到250。为什么是250呢这是因为JMeter每秒会发出100个请求每一个请求休眠2秒因此Tomcat需要200个工作线程来干活此外Tomcat还有一些其他线程用来处理网络通信和后台任务所以总数是250左右。第一阶段压测暂停后线程数又下降到40这是因为线程池会回收空闲线程。第二阶段测试开始后线程数涨到了420这是因为每个请求休眠了4秒同理我们看到第三阶段测试的线程数是620。
* 我们再来看CPU在三个阶段的测试中CPU的峰值始终比较稳定这是因为JMeter控制了总体的吞吐量因为服务端用来处理这些请求所需要消耗的CPU基本也是一样的。
* 各测试阶段的内存使用量略有增加,这是因为线程数增加了,创建线程也需要消耗内存。
从上面的测试结果我们可以得出一个结论对于一个Web应用来说下游服务的延迟越大Tomcat所需要的线程数越多但是CPU保持稳定。所以如果你在实际工作碰到线程数飙升但是CPU没有增加的情况这个时候你需要怀疑你的Web应用所依赖的下游服务是不是出了问题响应时间是否变长了。
## 本期精华
今天我们学习了Tomcat中的关键的性能指标以及如何监控这些指标主要有**吞吐量、响应时间、错误数、线程池、CPU以及JVM内存。**
在实际工作中我们需要通过观察这些指标来诊断系统遇到的性能问题找到性能瓶颈。如果我们监控到CPU上升这时我们可以看看吞吐量是不是也上升了如果是那说明正常如果不是的话可以看看GC的活动如果GC活动频繁并且内存居高不下基本可以断定是内存泄漏。
## 课后思考
请问工作中你如何监控Web应用的健康状态遇到性能问题的时候是如何做问题定位的呢
不知道今天的内容你消化得如何?如果还有疑问,请大胆的在留言区提问,也欢迎你把你的课后思考和心得记录下来,与我和其他同学一起讨论。如果你觉得今天有所收获,欢迎你把它分享给你的朋友。