不能用异常拦截器感知虚拟机核心水位,因二者属不同维度:异常拦截器处理throwable,而水位指标需通过mxbean主动采集;异常是过载结果而非信号,oom等error难以捕获,且此时降级已晚。
不能直接用异常拦截器去“感知虚拟机核心水位”。这是两类不同维度的机制:异常拦截器作用于方法调用链路,处理的是代码执行时抛出的 throwable;而虚拟机核心水位(如 cpu 使用率、内存使用率、线程数、gc 频次等)属于 jvm 运行时状态指标,需通过 mxbean 主动采集,无法靠异常触发来反推。
为什么不能靠异常来感知水位
运行时异常(如 OutOfMemoryError、ThreadDeath、频繁 RejectedExecutionException)往往是水位过载的结果,而非信号。等到抛出这些异常,系统通常已处于失稳边缘——此时降级为时已晚,且异常本身可能已破坏线程栈或破坏资源一致性。真实生产中,OOM 前几乎不会稳定抛出可捕获的异常供拦截器处理;而 OutOfMemoryError 是 Error,默认不被普通 catch (Exception) 捕获,拦截器也难以安全介入。
正确做法:分离感知与拦截,再协同决策
应将“水位感知”和“异常拦截”解耦,各自做好本职,再在业务开关层统一调度:
-
水位感知走独立监控线程:用
MemoryPoolMXBean监控老年代使用率、OperatingSystemMXBean获取 CPU load、ThreadMXBean统计活跃线程数,每 5–10 秒计算滑动平均值,写入一个线程安全的全局状态(如AtomicInteger waterLevel,0=正常,1=预警,2=高压) -
异常拦截器专注异常归因与标记:用 C# 12 拦截器或 Java 中的 AOP 切面,在关键 RPC/DB 调用处捕获
TimeoutException、IOException等,并打标(如记录 “lastRpcFailType=timeout”, “failCountIn1m=7”),不直接降级,只上报 -
降级开关由复合条件驱动:业务方法中轻量判断:
if (WaterLevel.get() >= 2 || RecentFailures.isHighRate()) { return fallback(); }
这样既避免了异常风暴下的重复降级,又防止水位虚高时误判
示例:Java 中融合水位与异常的降级判断
封装一个工具类,把两类信号收敛为布尔开关:
- 内存水位 > 85% 且持续 3 个采样周期 → 触发内存高压态
- 过去 60 秒内,
SocketTimeoutException出现 ≥ 5 次 → 触发网络抖动态 - 两者任一成立,且当前请求非支付/订单创建等核心路径 → 自动返回缓存或兜底数据
特别注意虚拟机层面的干扰项
华为云 Stack 环境中,虚拟机状态异常(如电源状态与实际不符)常由并发操作或底层异常引发,这类问题不会产生 JVM 层异常**,也不会反映在内存/CPU 指标里。它属于 IaaS 层故障,需依赖云平台事件(如 HSS 告警、Stack 控制台事件订阅)来感知,不能指望应用层拦截器发现。此时降级逻辑应基于外部健康信号(如服务注册中心心跳缺失、预设探活接口超时)而非 JVM 异常。











