上个月做中间件巡检,发现客户那套 TongWeb 7.0 跑了大半个月,堆内存已经涨到 92%,Full GC 从每小时一次变成三分钟一次,业务方反馈只有一句“系统最近有点慢”。这种国产 Java 中间件不像 Spring Boot 自带 Actuator,也不像 Tomcat 有全公开的 JMX 资料可以抄,想用 Prometheus(普罗米修斯)+ Grafana 把 TongWeb 80909 这个业务端口盯起来,最难的其实不是搭监控平台本身,而是搞清楚 TongWeb 把状态藏在哪里、怎么把指标抠出来。
这篇文章是我在一个企业项目里从零搭这套监控的全过程,覆盖系统层、JVM 层、HTTP 探活三层,末尾把四个踩过的坑和完整排查链路也写了。适合正在维护 TongWeb,或者面对同类国产中间件(金蝶 Apusic、宝兰德等)不知道从何下手的运维同学参考。全程是内网部署,不涉及什么花哨操作,照着一步步来就能跑通。
1. 为什么要给TongWeb加监控:被“系统有点慢”逼出来的方案
1.1 TongWeb 的监控难点不在“不懂”而在“没有口子”
TongWeb 是东方通出的企业级 Java 应用服务器,同类国外产品是 WebLogic、WebSphere,国内经常和 Tomcat 放一起对比。实际用起来,Tomcat 能干的事它都能干,但它多了一套域(domain)、部署包、管理控制台的机制,在政企类项目里属于核心中间件。
监控它的第一个难点是:Tomcat 有大量公开的 JMX 资料、有现成的 exporter 生态,TongWeb 没有官方 Prometheus exporter。网上搜“TongWeb + Prometheus”基本搜不到成体系的方案,能找到的也是零散配置。第二个难点是:TongWeb 自带控制台是有监控页面的,但那个页面只能“看当下”,历史数据保留有限,也没法对接外部告警平台。进程一挂,控制台本身也没了,等于监控工具和故障对象一起阵亡。
1.2 四层指标拆解:从端口到 JVM 按优先级盯
我的习惯是先把要盯的指标分四层,再决定用什么工具去采集。不要一上来就装一堆 exporter,先想清楚每一层回答什么问题:
- 存活层:TongWeb 监听端口(80909)能不能通?HTTP 返回码是不是正常?这层回答的是“业务入口还活着吗”。
- 系统层:宿主机 CPU、内存、磁盘、网络开销。中间件吃资源,宿主机的状态决定了它的上限。
- JVM 层:堆内存水位、GC 频率和耗时、活跃线程数、类加载数。这是中间件监控的重头戏,Full GC 频繁、堆涨满这类问题,只有这一层能看到。
- 业务层:连接池使用率、请求量、响应延迟。能拿到最好,拿不到不强求,因为 TongWeb 的 MBean 结构没有公开标准化文档,硬啃成本高。
1.3 80909 端口在这套方案里的定位
TongWeb 管理控制台默认端口是 9060,而标题里的 80909 一般是业务应用在 TongWeb 上配置的 HTTP 服务监听端口,也就是对外提供接口和页面的入口。监控 80909 意味着监控“用户实际访问的那道门”,比只看进程在不在更有意义。
我在这套方案里对 80909 的处理不是去抓它的流量数据,而是用探活方式盯它:定期发 HTTP 请求,看返回码、响应耗时。这样最直接地反映业务可用性,也不需要应用侧配合改造。如果你那边 80909 也同时开着多个 context,建议挑一两个轻量的健康检查路径来探,不要直接打重接口。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 采集方案选型:三个 exporter 组合,替代不存在的 TongWeb exporter
2.1 为什么不是 Zabbix,也不是自己写脚本
有些团队会用 Zabbix 盯中间件,我在早期也试过。Zabbix 是轮询加 agent 模式,优点是模板丰富,但对 TongWeb 这种没有标准监控插件的目标,照样要自己造键值,而且 Zabbix 画图的体验和 Grafana 差一大截。更关键的问题是:JVM 的堆内存、GC 这类指标,靠写脚本读 /proc 是拿不全的,必须走 JMX。
自己写脚本定时解析 jstat 日志也是一种路子,但脚本要处理采集、存储、画图、告警四件事,最后往往变成脚本比业务还复杂。Prometheus 的拉模式加上 metric 命名规范,天然适合这种“不知道目标什么时候会出新指标”的场景,采集端只要暴露一个 /metrics 端点就行。
2.2 三件套的分工:系统、JVM、探活
最终选型是三个 exporter 配合,每个负责一层,互不干扰:
| 采集组件 | 覆盖指标 | 侵入性 | 端口 | 部署成本 |
|---|---|---|---|---|
| node_exporter | 宿主机 CPU、内存、磁盘、网络 | 无 | 9100 | 低 |
| jmx_prometheus_javaagent | TongWeb JVM 堆内存、GC、线程、类加载 | 中(需改 JVM 启动参数) | 9090 | 中 |
| blackbox_exporter | 80909 端口 HTTP 探活、响应状态、耗时 | 无 | 9115 | 低 |
这个组合的优点是把“侵入性”控制在最小。node 和 blackbox 都是独立进程,挂了不影响 TongWeb;唯一碰 TongWeb 的只有 JMX agent,但它只是挂载在 JVM 里的一个探针,不改业务代码,不碰部署的应用。
2.3 我放弃的路线:TongWeb 自带监控对接
TongWeb 的控制台能显示连接池、请求量、JVM 状态,我当时也想过能不能直接从控制台接口拿数据。翻了半天文档发现:控制台的 REST 接口格式没有对外公开承诺,版本之间变化大,对接成本不可控。还有一条路是开 JMX 远程端口,用 jconsole 那种方式采集,但这个方案要在生产环境开放一个远程 JMX 端口,安全扫描基本过不去,政企类项目尤其不要碰。放弃这两条路线之后,老老实实回到“本地 javaagent + 内网 HTTP 抓取”的方案,这也是最稳妥的。
3. Prometheus 与 Grafana 安装启动:半小时搭好底座
3.1 版本与下载源选择
这里直接给结论:Prometheus 用 2.53.x 及以后版本,Grafana 用 10.x 或 11.x。不要用系统 yum/apt 仓库里的老版本,那些版本往往落后两三个大版本,一些新版 dashboards 的查询语法会不兼容。
下载走 GitHub Releases,如果服务器拉不动 GitHub,就用国内云厂商的镜像站(华为云、阿里云的软件镜像都有 Prometheus 相关包),或者让内网 Nexus 缓存一份。打包成 tar.gz 的解压就能跑,二进制对服务器没什么依赖,这在离线内网环境很省心。
3.2 Prometheus 用 systemd 跑起来
我习惯把 Prometheus 装在 /opt 下,数据目录单独建 /data/prometheus,用独立系统用户跑,避免用 root 跑监控程序。
bash复制wget https://github.com/prometheus/prometheus/releases/download/v2.53.0/prometheus-2.53.0.linux-amd64.tar.gz
tar -xzf prometheus-2.53.0.linux-amd64.tar.gz -C /opt
ln -s /opt/prometheus-2.53.0.linux-amd64 /opt/prometheus
useradd --no-create-home --shell /bin/false prometheus
mkdir -p /data/prometheus
chown -R prometheus:prometheus /opt/prometheus /data/prometheus
然后写 /etc/systemd/system/prometheus.service:
ini复制[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/opt/prometheus/prometheus \
--config.file=/opt/prometheus/prometheus.yml \
--storage.tsdb.path=/data/prometheus \
--web.listen-address=:9090 \
--web.enable-lifecycle
Restart=on-failure
[Install]
WantedBy=multi-user.target
注意 --web.enable-lifecycle 这个参数,后面改 scrape 配置要调 /-/reload 接口,不加这个参数 HTTP 重载会报 403。启动后 curl http://127.0.0.1:9090/-/ready 返回 Ready 就是正常。
3.3 Grafana 首次启动后必须做的三件事
Grafana 用 tar 包方式装,解压后直接跑二进制或封装 systemd 都行。官方下载链接是 https://dl.grafana.com/oss/release/grafana-10.4.0.linux-amd64.tar.gz,装完后默认监听 3000 端口。
第一次登录 http://监控机IP:3000,默认账号 admin/admin,系统会强制改密码。改完密码后我做三件事:确认数据目录权限(tar 包模式下数据默认落在解压目录下的 data 文件夹,如果是 systemd 跑,务必 chown 给运行用户);添加 Prometheus 数据源,URL 填 http://127.0.0.1:9090,点 Save & Test 确认连通;把默认的 Home Dashboard 换成一个临时用的测试看板,避免每次打开都是空白。
4. exporter 部署细节:node_exporter、JMX agent、blackbox 探活
4.1 node_exporter:宿主机基础指标
node_exporter 是最省事的一个,下载二进制解压,加个 systemd 就完事。生产环境里我一般监听 --web.listen-address=:9100,并且通过防火墙只放通监控服务器到它的访问,毕竟 9100 端口暴露了机器所有系统指标,不合适的网络策略等于把家底亮给内网所有人看。
bash复制wget https://github.com/prometheus/node_exporter/releases/download/v1.8.2/node_exporter-1.8.2.linux-amd64.tar.gz
tar -xzf node_exporter-1.8.2.linux-amd64.tar.gz -C /opt
ln -s /opt/node_exporter-1.8.2.linux-amd64 /opt/node_exporter
验证方式就一条:curl http://127.0.0.1:9100/metrics | grep node_cpu_seconds_total,能看到数据说明没问题。
4.2 JMX exporter:把探针塞进 TongWeb 进程
这是整套方案里唯一需要动 TongWeb 的地方,也是风险最高的一步。jmx_prometheus_javaagent 是一个 Java agent jar,挂在 TongWeb 启动参数里,进程起来后它会在本机开一个 HTTP 端口,把 JMX MBean 转成 Prometheus 文本格式。
先把 jar 和一个最小配置放到 TongWeb 服务器上,建议放 /opt/jmx_exporter/ 下:
yaml复制# /opt/jmx_exporter/tongweb.yml
rules:
- pattern: 'java.lang<type=Memory>'
name: java_lang_memory
type: GAUGE
labels:
area: "heap"
其实这个 yml 写不写规则都行,agent 自带默认采集器会吐出 jvm_memory_bytes_used、jvm_gc_collection_seconds 这一批指标。关键是 javaagent 参数的形式:
bash复制JAVA_OPTS="$JAVA_OPTS -javaagent:/opt/jmx_exporter/jmx_prometheus_javaagent-0.20.0.jar=9090:/opt/jmx_exporter/tongweb.yml"
改哪里要看你 TongWeb 的版本。TongWeb 7.0 有域(domain)模式,JVM 参数一般写在域目录下的 domain.xml 里,或者 bin 目录下的启动脚本里;8.0 有些版本支持在管理控制台的服务器配置里改 JVM 参数,改完会回写到配置文件。最稳的办法是找到启动脚本里调用 java 命令的那一行,确认 JAVA_OPTS 有没有被后面的逻辑重新赋值覆盖。我那次就是在脚本里 export 的位置加错了,参数根本没传进 java 进程,后面坑一节会细说。
改完必须重启 TongWeb。这里有个现实问题:生产环境重启中间件是要走变更窗口的,尤其政务类项目,中间件起来要半天时间。所以这一步建议提前跟业务确认窗口,把 jar 和 yml 先放到位,窗口期内一次改完。重启后用 curl http://127.0.0.1:9090/metrics 验证,能看见 jvm_memory_bytes_used{area="heap"} 这类指标就成了。
4.3 blackbox exporter:盯住 80909 的 HTTP 健康
blackbox exporter 的职责是“发起一个 HTTP 请求,把结果变成指标”。它本身不关心探的是谁,只要在配置里定义好探测模块。
下载解压后,修改配置文件 blackbox.yml:
yaml复制modules:
http_2xx:
prober: http
timeout: 5s
http:
method: GET
preferred_ip_protocol: ip4
valid_status_codes: [200, 301, 302]
这里有个容易踩的细节:很多业务应用访问根路径会 302 跳转到登录页,valid_status_codes 如果只写 200,探活会一直红。所以我把 301、302 也放进来,先保证“服务活着”这个判断准确,再谈登录态。但是要注意,探活路径如果能落到一个不需要鉴权的健康检查接口最好,比如有些应用会留一个 /monitor/health 或者静态页面路径,用它来探比打根路径靠谱得多。TongWeb 部署的应用里如果有没有这种路径,去找应用负责人要一个。
blackbox exporter 启动后默认监听 9115,验证方式:curl "http://127.0.0.1:9115/probe?module=http_2xx&target=http://127.0.0.1:80909/",看到 probe_success 1 就说明模块正常。
5. 打通数据流:prometheus.yml 抓取配置解析与 Targets 诊断
5.1 三段 scrape 配置的逐行解释
Prometheus 默认配置里只有监控自身的 job,我们把三个 exporter 的抓取配置追加进去。下面是完整的 prometheus.yml 核心片段:
yaml复制global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'tongweb-node'
static_configs:
- targets: ['192.168.10.5:9100']
labels:
service: 'tongweb'
- job_name: 'tongweb-jvm'
static_configs:
- targets: ['192.168.10.5:9090']
labels:
service: 'tongweb'
- job_name: 'tongweb-probe'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
- 'http://192.168.10.5:80909/'
- 'http://192.168.10.5:80909/health'
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: '192.168.10.5:9115'
前两个 job 简单,就是让 Prometheus 每隔 15 秒去 9100 和 9090 两个端口拉数据。第三个 job 是探活配置,要重点理解它的 relabel 逻辑:
targets 里写的是要探测的地址,即晓得的 80909 两个路径。Prometheus 拿到这个列表后,会先把原始地址放到 __address__,第一步 relabel 把 __address__ 复制给 __param_target,这样 blackbox exporter 收到请求时才知道要去探测谁。第二步把 __param_target 的值写到 instance 标签,这样在图表里能区分探的是哪个目标。第三步把 __address__ 强制替换成 blackbox exporter 自己的地址和端口,因为真正要抓取的是 9115 端口的 /probe 接口,而不是去抓 80909。
这套逻辑是 blackbox 的标准玩法,第一次接触会觉得绕,但拆开看就是“用 __address__ 当参数传,再换成真正采集地址”。
5.2 重载配置后如何快速诊断
改完 prometheus.yml 执行重载:
bash复制curl -X POST http://127.0.0.1:9090/-/reload
没有 --web.enable-lifecycle 的话这里会失败,要么补参数重启,要么用 kill -HUP $(pidof prometheus)。
重载后打开 Prometheus 的 Status > Targets 页面,正常情况下 tongweb-node、tongweb-jvm、tongweb-probe 三个 job 都应该是 UP 状态。点每个 target 的 endpoint 链接可以直接看 scrape 是否报错。最直接的验证是在 Graph 页面查一个指标,比如输入 up{job="tongweb-jvm"},如果能查出值为 1,说明数据流已经通了。
6. Grafana 看板实操:从现成模板到自建探活面板
6.1 现成模板能省一半时间,但别直接照抄
Grafana 的 Dashboards 市场里,node_exporter 的经典模板 ID 是 1860,导入后宿主机 CPU、内存、磁盘、网络基本都能看。JVM 的模板可以试 4701(JVM Micrometer)或者 12900(JVM Actuator),这两个模板设定的指标名前缀和 JMX exporter 不一定完全匹配,导入后大概率会有部分面板没数据。这时候不要慌,先看面板的 PromQL 用的指标名是什么,再去 Explore 里确认一下 JMX exporter 实际吐出来的指标名,把 selector 改过来就行。
我用 JMX exporter 的默认采集器时,常见指标名是 jvm_memory_bytes_used、jvm_memory_bytes_max、jvm_gc_collection_seconds、jvm_threads_current、jvm_classes_loaded,和 Prometheus Java client 的风格一致。如果你的版本吐出来的是 jvm_memory_used_bytes 这种带单位后缀的命名,把 PromQL 里的指标名对应替换即可。
6.2 手动创建 80909 探活面板的 PromQL
我建议探活面板不要依赖现成模板,直接手动建一个,逻辑最清晰。创建一个新的 Dashboard,加一个 Panel,Query 选 Prometheus 数据源,填入:
promql复制probe_success{job="tongweb-probe", instance="http://192.168.10.5:80909/"}
这个面板显示 1 代表探活成功,0 代表失败。再加一个面板看 HTTP 状态码:
promql复制probe_http_status_code{job="tongweb-probe"}
响应耗时用:
promql复制probe_duration_seconds{job="tongweb-probe"}
这三个面板组合起来,80909 端口的健康状态就一目了然了。
6.3 建议常驻的六个核心面板
| 面板名称 | PromQL 示例 | 看什么 |
|---|---|---|
| 80909 探活结果 | probe_success{job="tongweb-probe"} |
业务入口是否存活 |
| HTTP 状态码 | probe_http_status_code{job="tongweb-probe"} |
返回码是否异常 |
| 探测耗时 | probe_duration_seconds{job="tongweb-probe"} |
接口响应快慢 |
| JVM 堆使用率 | 100 * jvm_memory_bytes_used{area="heap"} / jvm_memory_bytes_max{area="heap"} |
堆水位,超过 85% 要警惕 |
| GC 耗时 | rate(jvm_gc_collection_seconds_sum[5m]) |
是否频繁 Full GC |
| 宿主机 CPU 空闲 | 100 - avg(rate(node_cpu_seconds_total{job="tongweb-node", mode="idle"}[5m])) * 100 |
宿主资源水位 |
这几块面板是日常巡检最先看的。比如说堆内存涨到 90% 以上,GC 曲线又频繁出现尖峰,基本可以判断是应用侧内存泄漏或配置偏小,下一步就该去看堆 dump 了。
7. 四个实战坑位的完整排查链路
7.1 坑一:JMX agent 挂载后 TongWeb 起不来
这是整套方案里最惊险的一次。在测试环境好好的,一到生产环境把 JAVA_OPTS 一加,TongWeb 直接启动失败,日志报 “Error opening zip file”。
排查链路我走了一遍:先确认 jar 文件是否存在、运行用户有没有读权限,ls -l /opt/jmx_exporter/ 一看没问题。再看 yml 路径,我配置里写的是 tongweb.yml,但实际文件是 tongweb-config.yml,路径对不上,JVM 会在启动阶段直接抛异常。改完文件名再启动,还是不行,这次日志报的是 Could not find or load main class。
后来用 bash -x 追踪启动脚本,发现 TongWeb 的脚本在解析参数时把我的 JAVA_OPTS 覆盖了,等于 javaagent 参数根本没传到 java 进程里。真正原因是:有些启动脚本里 JAVA_OPTS 是临时变量,下面有一段 JAVA_OPTS="" 重新赋值把前面的覆盖了。解决办法是把 javaagent 参数写在最终调用 java 命令前最后一行 export 的位置,或者直接写到 domain.xml 的 java-config 里,让中间件自己拼参数。
经验总结:改完启动参数后,先跑 bash -x startserver.sh 看传给 java 的实际命令行,比用眼睛检查脚本变量靠谱一百倍。生产窗口重启之前,先在测试环境完整演一遍。
7.2 坑二:Grafana 面板空白但 Targets 显示 up
数据源里 Prometheus targets 三个 job 全是 UP,Grafana 却一片空白。这个坑让我排查了半天,最后发现是数据源 URL 的问题:Grafana 和 Prometheus 装在同一台机器,我以为填 http://localhost:9090 没问题,但 Grafana 进程如果跑在容器或不同网络命名空间里,localhost 指向的是它自己。
排查链路:先在 Grafana 的 Explore 页面手输 probe_success,没有数据;再回 Prometheus Graph 页面查同一个指标,有数据。这就把问题缩小到了“数据源连接”而不是“抓取环节”。把数据源 URL 从 localhost 改成 Prometheus 实际监听的内网 IP,问题解决。
另外还有一个常见原因:导入的模板里带 $instance 变量,模板默认的实例列表和你的 instance 标签值对不上,选择器选不到数据。解决方法是编辑 Dashboard 变量,改成你实际 instance 值,或者把查询改成正则匹配。
7.3 坑三:探活一直红,浏览器却正常
blackbox 探活面板一直显示 0,但我用浏览器访问 http://192.168.10.5:80909/ 明明是好的。这个问题典型。
排查第一步,在主机上手动模拟 blackbox 的请求:curl -v http://127.0.0.1:80909/,发现返回的是 302,跳转到登录页。而我一开始的 valid_status_codes 只写了 [200],所以探活被认为是失败。把 302 加进白名单后,探活变成了绿色,但我心里不舒服——探活只证明“服务在跳转”,没证明“页面真的能用”。
于是换思路:找应用方要了一个不需要登录的轻量健康检查路径,比如 /monitor/health,返回 200 JSON。把探活 targets 改成这个路径,白名单保持 200。这样探活结果才是真正有意义的。如果你也遇到类似问题,优先去确认你们应用有没有这种免鉴权探活接口,探活路径比探活工具本身更重要。
7.4 坑四:夜间告警风暴,被定时任务和 exporter 自身坑了
探活告警配好不到一周,凌晨三点微信群炸了:probe 失败告警刷了十几条。爬起来一看,80909 确实短暂不可达,但业务侧没人报障。查 TongWeb 日志发现当天凌晨有个定时自动重启的任务,中间件重启那几分钟端口自然不通,恰好撞上探活周期。
这个问题的根源是告警规则太灵敏,for 时间设了 1 分钟太短。把探活告警的持续条件改成 5 分钟,中间件重启那种几十秒的抖动就不会触发。另外还有个隐藏的坑:如果 blackbox exporter 进程挂了,所有探活目标都会变成失败,告警会一次性全量轰炸。所以我额外加了一条针对 exporter 自身的告警:up{job="tongweb-probe"} == 0。这样“采集工具挂了”和“业务挂了”可以明确区分,不会稀里糊涂把两种故障混在一起。
8. 告警与推进建议:让监控真正变成守夜人
8.1 用 Grafana 自带的告警还是接 Alertmanager
Grafana 从 9 代开始自带的告警功能已经能独立用,配置告警规则、设置联系点、对接钉钉或企业微信 webhook 都行。如果你的环境只有一个监控实例、规则就几条,直接用 Grafana 告警最省事,不用多维护一个组件。
如果后面要管多套环境,或者告警路由规则复杂(比如不同团队接收不同告警),再上 Alertmanager。它的优势是分组、抑制、静默这些能力更成熟,但部署和维护也多一层成本。我的建议是:先把 Grafana 告警用起来,等规则超过十条再考虑迁移,不要一开始就把架构搞复杂。
8.2 三条最值得配的告警规则
监控闭环不能只“看”还要“报”。我实际运行下来,最有用的三条规则是这样的:
第一,80909 探活失败,持续 2 分钟触发严重告警。表达式用 probe_success{job="tongweb-probe"} == 0,for 设 2m。这里故意比 5 分钟短一点,因为如果真挂了 2 分钟业务已经能感知,要第一时间响应。
第二,JVM 堆使用率超过 85%,持续 10 分钟触发警告。表达式是 100 * jvm_memory_bytes_used{area="heap"} / jvm_memory_bytes_max{area="heap"} > 85。给 10 分钟的持续时间是为了过滤掉正常业务高峰的短暂内存抖动,如果是内存泄漏,10 分钟足够让它露馅。
第三,宿主机磁盘可用空间低于 10%,持续 5 分钟触发警告。磁盘满对中间件是致命问题,TongWeb 日志写不进去会直接卡死。表达式参考 node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"} < 0.1。这类系统告警平时不起眼,关键时刻能救命。
8.3 一些项目推进上的提醒
这套方案从开工到跑通,实际花了一天半,真正耗时的地方不在技术,而在流程:确认 TongWeb 重启窗口、申请应用健康检查路径、协调安全策略放行内网端口。如果是在政企类客户现场,提前把这些沟通项列出来走流程,比写配置快得多。
还有一点是我的个人习惯:告警配好后不要急着配上生产联系群,先发给自己一条测试消息,确认链路通了再说。探活告警触发一次之后,我要在 Grafana 里复查这条告警的“触发时长”是否合理,避免下半夜被自己造的规则吵醒。
最后提醒一句:监控数据是拿来用的,不是拿来截图的。这套 Prometheus + Grafana 的看板搭起来之后,真正有价值的是每周翻一次 JVM 堆趋势和 GC 曲线,在业务投诉之前发现异常。毕竟“系统有点慢”这句话,等真从业务嘴里说出来的时候,监控的意义已经减半了。
