Tomcat 企业级运维实战系列(四):Tomcat 企业级监控
🚀 Tomcat 系列文章导航
本系列系统讲解 Linux 环境下 Apache Tomcat 的部署、配置、管理与优化,并最终带你完成 企业级前后端分离项目上线。无论你是初学者还是想进阶的运维人员,这份路线图都能帮你快速构建完整的知识体系。
⚠️ 该系列所有涉及的软件包和项目都可以私信博主免费获取
- 👉 第一部分:Tomcat 核心概念与基础部署
- 👉 第二部分:Tomcat 系统化管理与应用部署
- 👉 第三部分:Tomcat 配置解析与集群化部署
- 👉 第四部分:Tomcat 企业级监控
- 👉 第五部分:Tomcat 优化和安全加固
- 👉 第六部分:综合项目实战:Java 前后端分离架构部署
一:监控工具
1)概述
可通过 Zabbix / Grafana / Prometheus 等工具监控 Tomcat/Java。
需要在 Tomcat 中开启 Java 远程监控功能(JMX Remote)。
也可在 Windows 安装 JDK,使用 jconsole / jvisualvm 工具远程连接 Tomcat 进行监控。
2)流程
在 Linux 修改 Tomcat 启动参数,开启 JMX 远程监控功能。
重启 Tomcat 使配置生效。
查看进程确认参数是否加载。
在 Windows 安装 JDK,并使用工具连接。
3)部署
修改
Tomcat
启动参数参数 说明 -Dcom.sun.management.jmxremote
开启远程监控功能 -Dcom.sun.management.jmxremote.port=12345
指定 JMX 端口 -Dcom.sun.management.jmxremote.authenticate=false
关闭认证 -Dcom.sun.management.jmxremote.ssl=false
关闭 SSL 加密 -Djava.rmi.server.hostname=192.168.2.104
绑定监听的 IP(通常填内网IP) [root@web01 ~]# vim /opt/module/tomcat-8.5.87/bin/catalina.sh
⚠️ 注意:Tomcat 8.5+ 中配置需用
\
换行。CATALINA_OPTS="$CATALINA_OPTS \ -Dcom.sun.management.jmxremote \ -Dcom.sun.management.jmxremote.port=12345 \ -Dcom.sun.management.jmxremote.authenticate=false \ -Dcom.sun.management.jmxremote.ssl=false \ -Djava.rmi.server.hostname=192.168.2.104"
启动
Tomcat
[root@web01 ~]# systemctl start tomcat
Window
安装JDK
配置环境变量:
JAVA_HOME=D:\...\jdk1.8.0_212
Path=%JAVA_HOME%\bin;
C:\Users\86152>java -version java version "1.8.0_212" Java(TM) SE Runtime Environment (build 1.8.0_212-b10) Java HotSpot(TM) 64-Bit Server VM (build 25.212-b10, mixed mode)
监控
使用
JConsole
监控路径:
D:\...\jdk1.8.0_212\bin\jconsole.exe
1. 打开 JConsole 2. 选择 远程连接 → 输入 IP:指定 JMX 端口 3. 如果关闭认证/SSL,则为“不安全连接” 4. 进入控制台即可查看 JVM 的内存、线程、类加载等信息
使用
JVisualVM
监控路径:
D:\...\jdk1.8.0_212\bin\jvisualvm.exe
1. 打开 JVisualVM 2. 添加远程主机 → 输入主机名/IP(不用带端口) 3. 添加 JMX 连接 → 输入 IP:指定 JMX 端口 4. 双击连接,即可查看监控效果
二:监控命令
1)jps
作用:查看正在运行的 Java 进程(类似
ps -ef | grep java
,但只显示 Java 进程)。常用命令
# 查看所有 Java 进程 jps # 查看详细信息(主类名、JVM参数等) jps -lvm | grep tomcat
2)jstack
作用:查看指定 Java 进程的线程信息,用于分析线程状态、死锁等问题。
操作步骤
线程状态说明:
- NEW:新建
- RUNNABLE:就绪
- RUNNING:运行中
- BLOCKED:阻塞(常见于 IO)
- DEAD:死亡
# 查看 Java 进程 PID jps 80177 Bootstrap # 查看该进程的线程栈信息 jstack 80177 # 仅查看线程状态 jstack 80177 | grep -i state
3)jmap
作用:查看或导出 JVM 内存信息。
常用命令
# 查看 JVM 内存使用情况 jmap -heap 80177 # 导出 JVM 内存镜像文件(用于分析) jmap -dump:format=b,file=8080.hprof 80177
4)MAT
工具分析
作用:图形化分析
.hprof
堆内存镜像文件,排查内存泄漏、对象占用过大等问题。使用步骤
在 Linux 导出堆文件:
jmap -dump:format=b,file=8080.hprof 80177 sz 8080.hprof # 传输到本地
Windows/Mac/Linux 安装
MAT
打开
.hprof
文件1. 欢迎页选择 Open a Heap Dump 2. 选择 Leak Suspects Report 查看内存泄漏报告
三:监控脚本
show-busy-java-thread.sh
显示当前环境中,所有繁忙的java线程. 以百分数显示使用率最高的前几个线程.
#!/bin/bash
# @Function
# Find out the highest cpu consumed threads of java, and print the stack of these threads.
#
# @Usage
# $ ./show-busy-java-threads.sh
#
# @author Jerry Lee
readonly PROG=`basename $0`
readonly -a COMMAND_LINE=("$0" "$@")
usage() {
cat <<EOF
Usage: ${PROG} [OPTION]...
Find out the highest cpu consumed threads of java, and print the stack of these threads.
Example: ${PROG} -c 10
Options:
-p, --pid find out the highest cpu consumed threads from the specifed java process,
default from all java process.
-c, --count set the thread count to show, default is 5
-h, --help display this help and exit
EOF
exit $1
}
readonly ARGS=`getopt -n "$PROG" -a -o c:p:h -l count:,pid:,help -- "$@"`
[ $? -ne 0 ] && usage 1
eval set -- "${ARGS}"
while true; do
case "$1" in
-c|--count)
count="$2"
shift 2
;;
-p|--pid)
pid="$2"
shift 2
;;
-h|--help)
usage
;;
--)
shift
break
;;
esac
done
count=${count:-5}
redEcho() {
[ -c /dev/stdout ] && {
# if stdout is console, turn on color output.
echo -ne "\033[1;31m"
echo -n "$@"
echo -e "\033[0m"
} || echo "$@"
}
yellowEcho() {
[ -c /dev/stdout ] && {
# if stdout is console, turn on color output.
echo -ne "\033[1;33m"
echo -n "$@"
echo -e "\033[0m"
} || echo "$@"
}
blueEcho() {
[ -c /dev/stdout ] && {
# if stdout is console, turn on color output.
echo -ne "\033[1;36m"
echo -n "$@"
echo -e "\033[0m"
} || echo "$@"
}
# Check the existence of jstack command!
if ! which jstack &> /dev/null; then
[ -z "$JAVA_HOME" ] && {
redEcho "Error: jstack not found on PATH!"
exit 1
}
! [ -f "$JAVA_HOME/bin/jstack" ] && {
redEcho "Error: jstack not found on PATH and $JAVA_HOME/bin/jstack file does NOT exists!"
exit 1
}
! [ -x "$JAVA_HOME/bin/jstack" ] && {
redEcho "Error: jstack not found on PATH and $JAVA_HOME/bin/jstack is NOT executalbe!"
exit 1
}
export PATH="$JAVA_HOME/bin:$PATH"
fi
readonly uuid=`date +%s`_${RANDOM}_$$
cleanupWhenExit() {
rm /tmp/${uuid}_* &> /dev/null
}
trap "cleanupWhenExit" EXIT
printStackOfThread() {
local line
local count=1
while IFS=" " read -a line ; do
local pid=${line[0]}
local threadId=${line[1]}
local threadId0x=`printf %x ${threadId}`
local user=${line[2]}
local pcpu=${line[4]}
local jstackFile=/tmp/${uuid}_${pid}
[ ! -f "${jstackFile}" ] && {
{
if [ "${user}" == "${USER}" ]; then
jstack ${pid} > ${jstackFile}
else
if [ $UID == 0 ]; then
sudo -u ${user} jstack ${pid} > ${jstackFile}
else
redEcho "[$((count++))] Fail to jstack Busy(${pcpu}%) thread(${threadId}/0x${threadId0x}) stack of java process(${pid}) under user(${user})."
redEcho "User of java process($user) is not current user($USER), need sudo to run again:"
yellowEcho " sudo ${COMMAND_LINE[@]}"
echo
continue
fi
fi
} || {
redEcho "[$((count++))] Fail to jstack Busy(${pcpu}%) thread(${threadId}/0x${threadId0x}) stack of java process(${pid}) under user(${user})."
echo
rm ${jstackFile}
continue
}
}
blueEcho "[$((count++))] Busy(${pcpu}%) thread(${threadId}/0x${threadId0x}) stack of java process(${pid}) under user(${user}):"
sed "/nid=0x${threadId0x} /,/^$/p" -n ${jstackFile}
done
}
ps -Leo pid,lwp,user,comm,pcpu --no-headers | {
[ -z "${pid}" ] &&
awk '$4=="java"{print $0}' ||
awk -v "pid=${pid}" '$1==pid,$4=="java"{print $0}'
} | sort -k5 -r -n | head --lines "${count}" | printStackOfThread
四:Java 系统负载与性能排查流程
1)初步观察系统负载
使用
w
/top
/uptime
查看系统整体负载如果 负载 > CPU 核心数 × 60%~70%,说明系统存在瓶颈
命令:
w uptime lscpu top -1
2)判断负载来源
CPU 负载
- 命令:
top
→ 观察%us
(用户态 CPU 占比) - 若 CPU 过高,说明是计算型压力
- 工具:
ps aux
/htop
/top
- 命令:
IO 负载
- 命令:
top
→ 观察%wa
(I/O 等待占比) - 若 IO 过高,说明磁盘或网络瓶颈
- 工具:
iotop
- 命令:
3)确定问题进程
- 使用
ps aux
/htop
/top
查找占用 CPU/内存较高的进程 - 获取 Java 进程 ID (PID)
4)进一步分析
查看日志
- 检查应用日志(如
catalina.out
),排查异常或错误
- 检查应用日志(如
线程分析
使用
jstack
查看线程堆栈信息关注线程状态(Runnable、Blocked、Waiting 等)
示例:
jstack <pid> jstack <pid> | grep -i state
JVM 内存分析
导出堆内存镜像:
jmap -dump:format=b,file=/root/jvm.hprof <pid>
查看 JVM 内存使用情况:
jmap -heap <pid>
堆文件分析
- 将
.hprof
文件传至本地 - 使用 MemoryAnalyzer Tool (MAT) 或 Eclipse MAT 插件打开
- 重点查看:
- Leak Suspects Report(内存泄漏嫌疑报告)
- 对象占用情况(哪些类实例过多/占用大)
- 将
5)问题解决与总结
- 根据
jstack
定位线程死锁/阻塞点 - 根据
jmap
+ MAT 分析内存泄漏问题 - 结合日志和监控,优化代码或调整 JVM 参数
- 验证问题是否解决
总结
📌 本节重点回顾
监控工具
学会了通过 JMX 远程监控,结合 JConsole、JVisualVM 等工具查看 JVM 内存、线程、类加载等运行状态。
掌握了在 Linux 修改 catalina.sh 启动参数 的方法,开启远程监控端口。
监控命令
jps → 查看 Java 进程
jstack → 分析线程堆栈,定位死锁/阻塞
jmap → 导出内存快照,分析堆使用情况
配合 MAT 工具 图形化分析 .hprof,排查内存泄漏
监控脚本
- 通过 show-busy-java-thread.sh 快速定位 最耗 CPU 的线程,并打印线程堆栈信息,便于排错。
性能排查流程
先看系统整体负载(w、top、uptime)
判断是 CPU 负载 还是 IO 负载
确定问题进程 → 结合 jstack / jmap 分析
使用 日志 + 堆分析工具(MAT) 找出瓶颈
优化代码、JVM 参数或系统资源配置
✅ 至此,你已经掌握了 Tomcat 与 JVM 的监控手段,能从 进程 → 线程 → 内存 → 系统层面 全方位排查性能问题。
下一篇将进入 Tomcat 优化与安全加固,进一步打造稳定、高性能的生产环境。