3、应用层监控:JVM监控(JMX)、HTTP接口监控、数据库连接池监控、业务指标埋点
应用层监控,说白了就是盯着你的代码跑得怎么样。我见过太多团队,基础设施监控做得漂漂亮亮,一到应用层面就两眼一抹黑。服务器CPU才用了20%,但用户就是反馈卡得要死——为什么?因为你的应用层出了问题。
这一章,我们重点聊四个方向:JVM监控、HTTP接口监控、数据库连接池监控、业务指标埋点。嗯,都是实战中绕不开的硬骨头。
核心观点:应用层监控是发现「慢」和「错」的第一道防线。基础设施监控告诉你机器还活着,应用层监控告诉你代码还健康。
3.1 JVM监控:从JMX到可视化
JVM监控,Java应用的老本行。我个人习惯用JMX(Java Management Extensions)作为数据源。你想想看,JVM内部那些堆内存、GC次数、线程数、类加载信息,JMX全都暴露出来了。
我在项目中遇到过一个问题:某次线上OOM,但监控系统只报了内存高,没告诉我具体是哪个区域爆了。后来我补上了JMX的详细指标,才发现是Metaspace泄漏。嗯,从那以后,我的JVM监控清单就固定下来了。
3.1.1 核心JVM指标
| 指标类别 | 具体指标 | 说明 |
|---|---|---|
| 堆内存 | Heap Used / Max / Committed | 看内存使用率,接近Max就要警惕 |
| 非堆内存 | Metaspace / CodeCache / Compressed Class Space | Metaspace泄漏是常见坑 |
| GC | Young GC次数/耗时, Full GC次数/耗时 | Full GC频繁=系统在「世界暂停」 |
| 线程 | 活跃线程数、阻塞线程数、死锁检测 | 死锁会导致服务完全卡死 |
| 类加载 | 已加载类总数、卸载类数 | 类加载过多可能是框架问题 |
我的小技巧:不要只监控Heap Used。我建议同时监控GC暂停时间。有一次我看到Heap Used才60%,但GC暂停时间已经飙到5秒了——说明对象晋升太快,老年代在频繁GC。
3.1.2 JMX接入实战
JMX接入其实不复杂。Spring Boot应用默认就开启了JMX,你只需要暴露端口就行。但要注意安全——我曾经见过有人把JMX端口直接暴露到公网,结果被人远程调用了System.exit()。
# application.yml 配置JMX
spring:
jmx:
enabled: true
default-domain: myapp
# JVM参数开启远程JMX(仅内网使用)
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=1099
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Djava.rmi.server.hostname=192.168.1.100
警告:生产环境一定要开启JMX认证!我见过不止一次因为JMX未认证导致的安全事故。至少加上用户名密码,或者用SSH隧道转发。
3.2 HTTP接口监控:不只是「通不通」
HTTP接口监控,很多人只盯着「200 OK」。但说实话,接口返回200不代表没问题。我遇到过接口返回200,但响应体是空的——因为业务逻辑抛了异常被全局捕获了,返回了一个空对象。
所以,我建议的HTTP监控要分三层:
- 可用性层:接口是否返回了预期的HTTP状态码(200、302等)
- 性能层:响应时间P50、P90、P99,以及超时率
- 正确性层:响应体结构是否完整,关键字段是否非空
3.2.1 接口监控的指标设计
我个人习惯用Prometheus的Histogram来记录接口响应时间。为什么?因为Histogram可以算百分位,比平均值靠谱多了。平均值会被长尾请求拉高,但P99能告诉你「最慢的那1%用户」体验如何。
# 伪代码:接口监控指标埋点
# 使用Micrometer + Prometheus
@Timed(value = "api.orders.create",
histogram = true,
percentiles = {0.5, 0.9, 0.99})
public Order createOrder(OrderRequest request) {
// 业务逻辑
}
关键点:接口监控的告警阈值不要用固定值。我建议用「同比/环比」的方式。比如:P99响应时间比昨天同一时段上涨了50%就告警。这样能自动适应业务高峰。
3.3 数据库连接池监控:被忽视的隐形杀手
数据库连接池,嗯,这个坑我踩过不止一次。连接池用完了,应用就卡死了——所有请求都在等连接,线程池也满了,最后整个应用雪崩。
你想想看,数据库连接池的监控其实很简单,但很多人就是不做。核心指标就几个:
- 活跃连接数:当前正在使用的连接
- 空闲连接数:池中空闲的连接
- 等待队列大小:有多少请求在等连接
- 连接获取耗时:从池中拿一个连接花了多久
3.3.1 HikariCP监控示例
HikariCP是目前最主流的连接池,它自带JMX监控。你只需要开启就行了。
# HikariCP配置开启JMX
spring:
datasource:
hikari:
pool-name: MyPool
register-mbeans: true # 开启JMX监控
# 通过JMX暴露的指标
# - HikariPool.MyPool.ActiveConnections
# - HikariPool.MyPool.IdleConnections
# - HikariPool.MyPool.PendingThreads
# - HikariPool.MyPool.ConnectionTimeoutRate
我的经验:连接池的「等待队列大小」是最敏感的指标。只要这个值大于0持续超过10秒,基本可以判定数据库有问题或者连接池太小了。我曾经就因为没监控这个指标,导致一次线上事故排查了整整4个小时。
3.4 业务指标埋点:让监控有「业务视角」
业务指标埋点,说白了就是把你业务里的关键事件变成数字。比如:订单创建量、支付成功率、用户注册数、购物车加购率。
我见过很多团队,技术指标监控得滴水不漏,但业务指标一片空白。结果呢?技术一切正常,但业务量跌了50%都没人发现——因为没人埋点。
3.4.1 埋点的最佳实践
我个人习惯用注解+AOP的方式做埋点。这样业务代码零侵入,维护起来也方便。
// 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BusinessMetric {
String name();
String[] tags() default {};
}
// AOP切面实现埋点
@Aspect
@Component
public class MetricAspect {
private final MeterRegistry meterRegistry;
@Around("@annotation(metric)")
public Object recordMetric(ProceedingJoinPoint pjp, BusinessMetric metric) throws Throwable {
long start = System.currentTimeMillis();
boolean success = true;
try {
return pjp.proceed();
} catch (Exception e) {
success = false;
throw e;
} finally {
long duration = System.currentTimeMillis() - start;
// 记录计数
meterRegistry.counter(metric.name() + ".total",
"success", String.valueOf(success)).increment();
// 记录耗时
meterRegistry.timer(metric.name() + ".duration")
.record(duration, TimeUnit.MILLISECONDS);
}
}
}
// 使用示例
@BusinessMetric(name = "order.create", tags = {"channel", "web"})
public Order createOrder(OrderRequest request) {
// 业务逻辑
}
核心原则:业务指标埋点要遵循「三不原则」——不影响业务性能、不侵入业务逻辑、不增加运维复杂度。用AOP或者消息队列异步上报都是好办法。
3.5 本章知识体系总览
下面这张图,是我梳理的应用层监控核心逻辑。你可以看到,四个监控维度各有侧重,但最终都汇聚到「告警」和「可视化」两个出口。
总结一下:应用层监控的核心就四个字——「由内而外」。JVM是应用的内核,HTTP是应用的接口,连接池是应用的骨架,业务指标是应用的血肉。缺一个,你的监控体系就不完整。
我曾经帮一个团队做监控体系重构,他们之前只做了HTTP接口监控。结果呢?接口响应时间突然变慢,查了半天才发现是JVM Full GC导致的。从那以后,我坚持「四件套」必须一起上。
公众号:蓝海资料掘金营,微信deep3321