当前位置: 首页 > news >正文

第八章-Tomcat调试与监控

🧰 第八章:Tomcat 调试与监控

目录

  • 8.1 日志系统详解
  • 8.2 监控方式与工具
  • 8.3 JMX 监控实现
  • 8.4 性能监控与诊断
  • 8.5 故障排查与调试
  • 8.6 自动化监控方案
  • 8.7 本章小结

8.1 日志系统详解

8.1.1 日志文件结构

日志文件说明
日志文件说明位置用途
catalina.out主日志文件logs/记录 Tomcat 启动、停止和错误信息
catalina.yyyy-mm-dd.log按日期分割的日志logs/每日的详细日志
localhost.yyyy-mm-dd.log本地主机日志logs/本地主机的详细日志
manager.yyyy-mm-dd.log管理应用日志logs/Manager 应用的日志
host-manager.yyyy-mm-dd.log主机管理日志logs/Host Manager 应用的日志
localhost_access_log.yyyy-mm-dd.txt访问日志logs/HTTP 请求访问日志
日志配置
<!-- 日志配置 -->
<Context path="/myapp" docBase="myapp"><Valve className="org.apache.catalina.valves.AccessLogValve"directory="logs"prefix="localhost_access_log"suffix=".txt"pattern="%h %l %u %t &quot;%r&quot; %s %b %D"resolveHosts="false" />
</Context>

8.1.2 日志级别配置

日志级别设置
# logging.properties
# 根日志级别
.level = INFO# 包级别日志
org.apache.catalina.level = INFO
org.apache.coyote.level = INFO
org.apache.tomcat.level = INFO
org.apache.catalina.startup.level = INFO
org.apache.catalina.core.level = INFO
org.apache.catalina.session.level = INFO
org.apache.catalina.connector.level = INFO
org.apache.catalina.valves.level = INFO
org.apache.catalina.authenticator.level = INFO
org.apache.catalina.realm.level = INFO
org.apache.catalina.loader.level = INFO
org.apache.catalina.util.level = INFO
org.apache.catalina.mbeans.level = INFO
org.apache.catalina.storeconfig.level = INFO
org.apache.catalina.ha.level = INFO
org.apache.catalina.tribes.level = INFO
org.apache.catalina.cluster.level = INFO
org.apache.catalina.ha.tcp.level = INFO
org.apache.catalina.ha.session.level = INFO
org.apache.catalina.ha.deploy.level = INFO
org.apache.catalina.ha.context.level = INFO
org.apache.catalina.ha.backup.level = INFO
org.apache.catalina.ha.channel.level = INFO
org.apache.catalina.ha.channel.sender.level = INFO
org.apache.catalina.ha.channel.receiver.level = INFO
org.apache.catalina.ha.channel.interceptor.level = INFO
org.apache.catalina.ha.channel.interceptor.tcp.level = INFO
org.apache.catalina.ha.channel.interceptor.tcp.TcpFailureDetector.level = INFO
org.apache.catalina.ha.channel.interceptor.tcp.TcpPingInterceptor.level = INFO
org.apache.catalina.ha.channel.interceptor.tcp.TcpValve.level = INFO
org.apache.catalina.ha.channel.interceptor.tcp.TcpFailureDetector.level = INFO
org.apache.catalina.ha.channel.interceptor.tcp.TcpPingInterceptor.level = INFO
org.apache.catalina.ha.channel.interceptor.tcp.TcpValve.level = INFO

8.1.3 自定义日志格式

访问日志格式
<!-- 自定义访问日志格式 -->
<Valve className="org.apache.catalina.valves.AccessLogValve"directory="logs"prefix="localhost_access_log"suffix=".txt"pattern="%h %l %u %t &quot;%r&quot; %s %b %D %{User-Agent}i %{Referer}i"resolveHosts="false" />
日志格式说明
格式符说明示例
%h远程主机名或 IP192.168.1.100
%l远程逻辑用户名-
%u远程用户admin
%t时间戳[25/Dec/2023:10:30:45 +0800]
%r请求行GET /myapp/servlet HTTP/1.1
%s状态码200
%b响应字节数1024
%D处理时间(毫秒)150
%{User-Agent}i用户代理Mozilla/5.0…
%{Referer}i引用页面http://example.com/

8.2 监控方式与工具

8.2.1 内置监控工具

Manager 应用监控
<!-- 启用 Manager 应用 -->
<Context path="/manager" docBase="manager"privileged="true" antiResourceLocking="false" antiJARLocking="false"><Valve className="org.apache.catalina.valves.RemoteAddrValve"allow="127\.0\.0\.1|::1|0:0:0:0:0:0:0:1" />
</Context>
Manager 功能
  • 应用管理:部署、启动、停止、重新加载应用
  • 会话管理:查看和管理用户会话
  • 资源管理:查看系统资源使用情况
  • 性能监控:查看请求统计和性能指标

8.2.2 外部监控工具

VisualVM 监控
// VisualVM 监控配置
public class VisualVMMonitor {public void monitorTomcat() {// 1. 获取 MBean 服务器MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();// 2. 监控线程池try {ObjectName threadPoolName = new ObjectName("Catalina:type=ThreadPool,name=*");Set<ObjectName> threadPools = mBeanServer.queryNames(threadPoolName, null);for (ObjectName threadPool : threadPools) {int currentThreadCount = (Integer) mBeanServer.getAttribute(threadPool, "currentThreadCount");int currentThreadsBusy = (Integer) mBeanServer.getAttribute(threadPool, "currentThreadsBusy");int maxThreads = (Integer) mBeanServer.getAttribute(threadPool, "maxThreads");System.out.println("Thread Pool: " + threadPool.getKeyProperty("name"));System.out.println("Current Threads: " + currentThreadCount);System.out.println("Busy Threads: " + currentThreadsBusy);System.out.println("Max Threads: " + maxThreads);}} catch (Exception e) {e.printStackTrace();}}
}
JConsole 监控
// JConsole 监控配置
public class JConsoleMonitor {public void monitorTomcat() {// 1. 获取内存使用情况MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();System.out.println("Heap Memory: " + heapUsage.getUsed() / 1024 / 1024 + "MB");System.out.println("Non-Heap Memory: " + nonHeapUsage.getUsed() / 1024 / 1024 + "MB");// 2. 获取线程信息ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();int threadCount = threadBean.getThreadCount();int peakThreadCount = threadBean.getPeakThreadCount();long totalStartedThreadCount = threadBean.getTotalStartedThreadCount();System.out.println("Thread Count: " + threadCount);System.out.println("Peak Thread Count: " + peakThreadCount);System.out.println("Total Started Thread Count: " + totalStartedThreadCount);// 3. 获取 GC 信息List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();for (GarbageCollectorMXBean gcBean : gcBeans) {System.out.println("GC Name: " + gcBean.getName());System.out.println("GC Count: " + gcBean.getCollectionCount());System.out.println("GC Time: " + gcBean.getCollectionTime() + "ms");}}
}

8.2.3 第三方监控工具

Prometheus + Grafana 监控
# prometheus.yml
global:scrape_interval: 15sscrape_configs:- job_name: 'tomcat'static_configs:- targets: ['localhost:8080']metrics_path: '/actuator/prometheus'scrape_interval: 5s
监控指标配置
// 监控指标配置
@Component
public class TomcatMetrics {private final MeterRegistry meterRegistry;public TomcatMetrics(MeterRegistry meterRegistry) {this.meterRegistry = meterRegistry;}@EventListenerpublic void handleRequest(RequestHandledEvent event) {// 记录请求指标Timer.Sample sample = Timer.start(meterRegistry);sample.stop(Timer.builder("tomcat.request.duration").tag("method", event.getMethod()).tag("status", String.valueOf(event.getStatusCode())).register(meterRegistry));// 记录请求计数Counter.builder("tomcat.request.count").tag("method", event.getMethod()).tag("status", String.valueOf(event.getStatusCode())).register(meterRegistry).increment();}
}

8.3 JMX 监控实现

8.3.1 JMX 配置

启用 JMX 监控
<!-- JMX 配置 -->
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener"rmiRegistryPortPlatform="10099"rmiServerPortPlatform="10000" />
JMX 参数配置
# JMX 参数配置
export JAVA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=10099 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"

8.3.2 JMX 监控实现

线程池监控
// 线程池监控
public class ThreadPoolMonitor {private final MBeanServer mBeanServer;public ThreadPoolMonitor() {this.mBeanServer = ManagementFactory.getPlatformMBeanServer();}public void monitorThreadPools() {try {ObjectName threadPoolName = new ObjectName("Catalina:type=ThreadPool,name=*");Set<ObjectName> threadPools = mBeanServer.queryNames(threadPoolName, null);for (ObjectName threadPool : threadPools) {int currentThreadCount = (Integer) mBeanServer.getAttribute(threadPool, "currentThreadCount");int currentThreadsBusy = (Integer) mBeanServer.getAttribute(threadPool, "currentThreadsBusy");int maxThreads = (Integer) mBeanServer.getAttribute(threadPool, "maxThreads");int minSpareThreads = (Integer) mBeanServer.getAttribute(threadPool, "minSpareThreads");int maxSpareThreads = (Integer) mBeanServer.getAttribute(threadPool, "maxSpareThreads");System.out.println("Thread Pool: " + threadPool.getKeyProperty("name"));System.out.println("Current Threads: " + currentThreadCount);System.out.println("Busy Threads: " + currentThreadsBusy);System.out.println("Max Threads: " + maxThreads);System.out.println("Min Spare Threads: " + minSpareThreads);System.out.println("Max Spare Threads: " + maxSpareThreads);}} catch (Exception e) {e.printStackTrace();}}
}
内存监控
// 内存监控
public class MemoryMonitor {private final MBeanServer mBeanServer;public MemoryMonitor() {this.mBeanServer = ManagementFactory.getPlatformMBeanServer();}public void monitorMemory() {try {ObjectName memoryName = new ObjectName("java.lang:type=Memory");CompositeData heapUsage = (CompositeData) mBeanServer.getAttribute(memoryName, "HeapMemoryUsage");CompositeData nonHeapUsage = (CompositeData) mBeanServer.getAttribute(memoryName, "NonHeapMemoryUsage");long heapUsed = (Long) heapUsage.get("used");long heapMax = (Long) heapUsage.get("max");long nonHeapUsed = (Long) nonHeapUsage.get("used");long nonHeapMax = (Long) nonHeapUsage.get("max");System.out.println("Heap Memory: " + heapUsed / 1024 / 1024 + "MB / " + heapMax / 1024 / 1024 + "MB");System.out.println("Non-Heap Memory: " + nonHeapUsed / 1024 / 1024 + "MB / " + nonHeapMax / 1024 / 1024 + "MB");} catch (Exception e) {e.printStackTrace();}}
}

8.3.3 自定义 MBean

自定义 MBean 接口
// 自定义 MBean 接口
public interface TomcatMonitorMBean {int getActiveConnections();int getTotalRequests();double getAverageResponseTime();void resetCounters();
}
自定义 MBean 实现
// 自定义 MBean 实现
public class TomcatMonitor implements TomcatMonitorMBean {private final AtomicInteger activeConnections = new AtomicInteger(0);private final AtomicLong totalRequests = new AtomicLong(0);private final AtomicLong totalResponseTime = new AtomicLong(0);@Overridepublic int getActiveConnections() {return activeConnections.get();}@Overridepublic int getTotalRequests() {return totalRequests.intValue();}@Overridepublic double getAverageResponseTime() {long requests = totalRequests.get();if (requests == 0) {return 0.0;}return (double) totalResponseTime.get() / requests;}@Overridepublic void resetCounters() {totalRequests.set(0);totalResponseTime.set(0);}public void recordRequest(long responseTime) {totalRequests.incrementAndGet();totalResponseTime.addAndGet(responseTime);}
}
注册自定义 MBean
// 注册自定义 MBean
public class MBeanRegistration {public void registerMBean() {try {MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();ObjectName objectName = new ObjectName("com.example:type=TomcatMonitor");TomcatMonitor monitor = new TomcatMonitor();mBeanServer.registerMBean(monitor, objectName);} catch (Exception e) {e.printStackTrace();}}
}

8.4 性能监控与诊断

8.4.1 性能指标监控

关键性能指标
指标说明正常范围告警阈值
响应时间平均响应时间< 100ms> 500ms
吞吐量每秒请求数> 1000 req/s< 500 req/s
并发连接数当前连接数< 1000> 2000
线程池使用率线程池使用率< 80%> 90%
内存使用率堆内存使用率< 80%> 90%
GC 频率GC 频率< 10次/分钟> 50次/分钟
性能监控实现
// 性能监控实现
@Component
public class PerformanceMonitor {private final AtomicLong requestCount = new AtomicLong(0);private final AtomicLong totalResponseTime = new AtomicLong(0);private final AtomicLong maxResponseTime = new AtomicLong(0);private final AtomicLong minResponseTime = new AtomicLong(Long.MAX_VALUE);@EventListenerpublic void handleRequest(RequestHandledEvent event) {long responseTime = event.getProcessingTimeMillis();requestCount.incrementAndGet();totalResponseTime.addAndGet(responseTime);// 更新最大响应时间long currentMax = maxResponseTime.get();while (responseTime > currentMax && !maxResponseTime.compareAndSet(currentMax, responseTime)) {currentMax = maxResponseTime.get();}// 更新最小响应时间long currentMin = minResponseTime.get();while (responseTime < currentMin && !minResponseTime.compareAndSet(currentMin, responseTime)) {currentMin = minResponseTime.get();}}public PerformanceStats getStats() {long count = requestCount.get();long total = totalResponseTime.get();long max = maxResponseTime.get();long min = minResponseTime.get();return new PerformanceStats(count, total / count, max, min);}
}

8.4.2 内存泄漏检测

内存泄漏检测
// 内存泄漏检测
public class MemoryLeakDetector {private final MemoryMXBean memoryBean;private final ScheduledExecutorService scheduler;public MemoryLeakDetector() {this.memoryBean = ManagementFactory.getMemoryMXBean();this.scheduler = Executors.newScheduledThreadPool(1);startMonitoring();}private void startMonitoring() {scheduler.scheduleAtFixedRate(() -> {MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();long used = heapUsage.getUsed();long max = heapUsage.getMax();double usagePercent = (double) used / max * 100;if (usagePercent > 80) {System.out.println("Warning: High memory usage: " + usagePercent + "%");// 触发内存分析analyzeMemoryUsage();}}, 0, 30, TimeUnit.SECONDS);}private void analyzeMemoryUsage() {// 分析内存使用情况MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();System.out.println("Memory Analysis:");System.out.println("Used: " + heapUsage.getUsed() / 1024 / 1024 + "MB");System.out.println("Max: " + heapUsage.getMax() / 1024 / 1024 + "MB");System.out.println("Usage: " + (double) heapUsage.getUsed() / heapUsage.getMax() * 100 + "%");}
}

8.4.3 线程死锁检测

线程死锁检测
// 线程死锁检测
public class DeadlockDetector {private final ThreadMXBean threadBean;private final ScheduledExecutorService scheduler;public DeadlockDetector() {this.threadBean = ManagementFactory.getThreadMXBean();this.scheduler = Executors.newScheduledThreadPool(1);startMonitoring();}private void startMonitoring() {scheduler.scheduleAtFixedRate(() -> {long[] deadlockedThreads = threadBean.findDeadlockedThreads();if (deadlockedThreads != null && deadlockedThreads.length > 0) {System.out.println("Deadlock detected in threads: " + Arrays.toString(deadlockedThreads));// 获取死锁线程信息ThreadInfo[] threadInfos = threadBean.getThreadInfo(deadlockedThreads);for (ThreadInfo threadInfo : threadInfos) {System.out.println("Deadlocked thread: " + threadInfo.getThreadName());System.out.println("Thread state: " + threadInfo.getThreadState());System.out.println("Lock info: " + threadInfo.getLockInfo());}}}, 0, 10, TimeUnit.SECONDS);}
}

8.5 故障排查与调试

8.5.1 常见故障类型

故障分类
故障类型症状原因解决方案
内存溢出OutOfMemoryError内存不足增加堆内存,优化代码
线程池耗尽请求超时线程池配置不当调整线程池参数
连接超时连接失败网络问题检查网络配置
应用启动失败启动异常配置错误检查配置文件
性能下降响应慢资源不足优化配置,增加资源

8.5.2 故障诊断工具

线程转储分析
# 生成线程转储
jstack <pid> > thread_dump.txt# 分析线程转储
grep "BLOCKED" thread_dump.txt
grep "WAITING" thread_dump.txt
grep "RUNNABLE" thread_dump.txt
堆转储分析
# 生成堆转储
jmap -dump:format=b,file=heap_dump.hprof <pid># 分析堆转储
jhat heap_dump.hprof

8.5.3 调试技巧

远程调试配置
# 远程调试参数
export JAVA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"
日志级别调整
# 调试日志配置
org.apache.catalina.level = DEBUG
org.apache.coyote.level = DEBUG
org.apache.tomcat.level = DEBUG

8.6 自动化监控方案

8.6.1 监控脚本

健康检查脚本
#!/bin/bash
# health_check.sh# 检查 Tomcat 进程
if ! pgrep -f "tomcat" > /dev/null; thenecho "Tomcat process not found"exit 1
fi# 检查端口
if ! netstat -ln | grep :8080 > /dev/null; thenecho "Port 8080 not listening"exit 1
fi# 检查 HTTP 响应
if ! curl -f http://localhost:8080/ > /dev/null 2>&1; thenecho "HTTP response failed"exit 1
fiecho "Tomcat is healthy"
exit 0
性能监控脚本
#!/bin/bash
# performance_monitor.sh# 获取性能指标
THREAD_COUNT=$(ps -eLf | grep tomcat | wc -l)
MEMORY_USAGE=$(ps -o pid,vsz,rss,comm -p $(pgrep -f tomcat) | tail -1 | awk '{print $3}')
CPU_USAGE=$(ps -o pid,pcpu,comm -p $(pgrep -f tomcat) | tail -1 | awk '{print $2}')echo "Thread Count: $THREAD_COUNT"
echo "Memory Usage: $MEMORY_USAGE KB"
echo "CPU Usage: $CPU_USAGE%"# 检查阈值
if [ $THREAD_COUNT -gt 1000 ]; thenecho "Warning: High thread count"
fiif [ $MEMORY_USAGE -gt 1000000 ]; thenecho "Warning: High memory usage"
fiif [ $(echo "$CPU_USAGE > 80" | bc) -eq 1 ]; thenecho "Warning: High CPU usage"
fi

8.6.2 告警机制

邮件告警
// 邮件告警
@Component
public class AlertService {@Autowiredprivate JavaMailSender mailSender;public void sendAlert(String subject, String message) {try {MimeMessage mimeMessage = mailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);helper.setTo("admin@example.com");helper.setSubject(subject);helper.setText(message);mailSender.send(mimeMessage);} catch (Exception e) {e.printStackTrace();}}
}
短信告警
// 短信告警
@Component
public class SmsAlertService {public void sendSmsAlert(String message) {// 发送短信告警System.out.println("SMS Alert: " + message);}
}

8.6.3 监控面板

监控面板实现
// 监控面板
@RestController
@RequestMapping("/monitor")
public class MonitorController {@Autowiredprivate PerformanceMonitor performanceMonitor;@GetMapping("/stats")public PerformanceStats getStats() {return performanceMonitor.getStats();}@GetMapping("/health")public Map<String, Object> getHealth() {Map<String, Object> health = new HashMap<>();health.put("status", "UP");health.put("timestamp", System.currentTimeMillis());return health;}
}

8.7 本章小结

关键要点

  1. 日志系统

    • 了解各种日志文件的作用
    • 配置合适的日志级别
    • 自定义日志格式
  2. 监控方式

    • 使用内置监控工具
    • 集成外部监控工具
    • 实现自定义监控
  3. JMX 监控

    • 配置 JMX 监控
    • 实现自定义 MBean
    • 监控关键指标
  4. 性能监控

    • 监控关键性能指标
    • 检测内存泄漏
    • 检测线程死锁
  5. 故障排查

    • 识别常见故障类型
    • 使用诊断工具
    • 应用调试技巧
  6. 自动化监控

    • 实现监控脚本
    • 建立告警机制
    • 创建监控面板

最佳实践

  1. 监控策略

    • 建立全面的监控体系
    • 设置合理的告警阈值
    • 定期分析监控数据
  2. 故障处理

    • 建立故障处理流程
    • 准备应急处理方案
    • 记录故障处理经验
  3. 性能优化

    • 持续监控性能指标
    • 及时调整配置参数
    • 优化应用代码

下一步学习

在下一章中,我们将深入探讨 Tomcat 的源码阅读与扩展,了解 Tomcat 的核心实现原理,以及如何通过自定义组件扩展 Tomcat 的功能。


相关资源

  • Tomcat 监控指南
  • JMX 监控文档
  • VisualVM 使用指南
http://www.dtcms.com/a/523602.html

相关文章:

  • 算法基础篇(8)贪心算法
  • 第二章-Tomcat核心架构拆解
  • 带你深度了解作用域和闭包
  • 【Mac下通过Brew安装Ollama 】部署 DeepSeek 轻量模型(实测版)
  • 微信网站用什么语言开发wordpress4.9.4 安装
  • 如何在百度提交自己的网站简要列举网站常见类型
  • 机器视觉HALCON:5.图像标定
  • 【跟小嘉学习JavaWeb开发】第三章 从数据类型说起
  • CTF WEB入门 爆破篇
  • NAT网络地址转换
  • 【自然语言处理】预训练01:词嵌入(word2vec)
  • 利用inscode帮我用前端页面展示分析博客数据
  • 「赤兔」Chitu 框架深度解读(十):任务调度与并发控制策略
  • Java CompletableFuture 详解与实战:让异步编程更优雅
  • 建设外贸网站要多少钱建设局办的焊工证全国通用吗
  • Linux_基础IO(2)
  • Docker 中使用Nginx 一个端口启动多个前端项目
  • S9 顺序队列
  • 函数绑定器 std::bind
  • STM32基本定时器
  • 第9部分-性能优化、调试与并发设计模式
  • 编程素养提升之EffectivePython(Builder篇)
  • Vue 3 + TypeScript 项目性能优化全链路实战:从 2.1MB 到 130KB 的蜕变
  • 网站首页图腾讯 云上做网站教程
  • Ubuntu(Linux)安装更好用的中文输入法
  • 《算法闯关指南:优选算法--二分查找》--23.寻找旋转排序数组中的最小值,24.点名
  • 【ssh密钥】--- 当密钥密码遇见 Git 服务器:一场关于 “信任” 的浪漫喜剧
  • kotlin 数据类的get和set 问题
  • 爱站网功能左旗网站建设
  • 中国企业跨境云组网指南:低延迟访问德国AWS云做数据分析的实操方案