06 混沌工程中服务层故障演练

这张图片详细记录了一次针对 Java 应用程序的JVM 故障注入实验。实验的核心目的是通过人为制造 JVM CPU 满载 和 内存溢出(OOM),来验证分布式系统在极端条件下的容错能力和监控系统的有效性。
以下是图片中各个具体操作命令的详细含义分析:
1. 环境准备与故障注入准备阶段
命令 1:ps -aux | grep DBPlus-0.0.1-SNAPSHOT.jar
-
含义:查找目标 Java 应用程序的进程 ID(PID)。
-
作用:在进行任何故障注入前,必须先获取服务进程的 PID。图片中查找到的目标 PID 为 16057。
命令 2:blade prepare jvm --pid 16057
-
含义:这是 ChaosBlade 的准备命令,针对指定的 PID(16057)挂载 Java Agent。
-
作用:JVM 故障注入(如 OOM、CPU 满载)需要深入 JVM 内部进行操作,该命令在目标进程启动字节码增强功能,为后续的故障注入打通通道。返回的
eb1096c754f14f8c是该准备操作的唯一 UID。
2. 核心实验:模拟 JVM CPU 挂满
命令 3:blade c jvm oom --area HEAP --wild-mode true --process java --timeout 8000
-
含义:注入一个 堆内存溢出(OutOfMemoryError) 的故障。
-
参数解析:
-
blade c:create的简写。 -
--area HEAP:指定故障发生区域为堆内存(Java 对象存储的区域)。 -
--wild-mode true:全量模式。表示不仅当前内存不足时报错,而是强制不断申请内存,即使 JVM 通过 GC 清理了空间也继续申请,直到抛出 OOM。 -
--timeout 8000:故障注入持续 8000 毫秒(8秒)后自动停止。
-
-
预期现象:Java 进程会频繁触发 Full GC,随后抛出
java.lang.OutOfMemoryError: Java heap space异常,导致服务不可用。
3. 监控与诊断命令(JVM-CPU 挂满场景)
在触发 OOM 后,图片展示了如何使用 VisualVM 和 网络工具 进行监控,这是排查问题的关键手段:
命令 4:Java VisualVM 启动命令
bash
java -Djava.rmi.server.hostname=101.43.158.84 -Dcom.sun.management.jmxremote ... -jar DBPlus-0.0.1-SNAPSHOT.jar
-
含义:通过添加 JMX 参数重新启动服务,以便远程监控 JVM 状态。
-
参数解析:
-
-Dcom.sun.management.jmxremote:开启 JMX 远程监控功能。 -
-Djava.rmi.server.hostname=101.43.158.84:指定 RMI 绑定的主机 IP,确保监控工具能连上。 -
-Dcom.sun.management.jmxremote.port=1099:指定 JMX 监控端口。 -
-Dcom.sun.management.jmxremote.authenticate=false:关闭认证(测试环境专用)。 -
-Dcom.sun.management.jmxremote.ssl=false:关闭 SSL 加密(测试环境专用)。
-
-
作用:允许运维人员使用
jvisualvm工具实时查看堆内存曲线、GC 频率、线程状态和 CPU 占用率。
命令 5:lsof -i | grep java
-
含义:列出所有与 Java 进程相关的网络连接。
-
作用:排查网络层面的问题。
-
*:33745 (LISTEN):Java 进程正在监听本地端口,通常用于 JMX 监控(对应上面的 1099 端口)或 RMI。 -
k8s-node1:rmiregistry->36.47.129.195:12176 (ESTABLISHED):显示 Java 进程与外部 IP 建立了 RMI 连接(用于接收 ChaosBlade 的故障指令)。 -
k8s-node1:60524->k8s-node1:mysql (ESTABLISHED):核心业务连接。说明 Java 应用正在正常连接 MySQL 数据库,可用于确认服务间的通信状态。
-
4. 进阶实验:模拟 CPU 满载
命令 6:blade create jvm cpufullload --cpu-count 6 --pid 8261
-
含义:注入 CPU 满载 故障。
-
参数解析:
-
--cpu-count 6:指定占用 6 个 CPU 核心。 -
--pid 8261:对另一个 PID 为 8261 的 Java 进程进行操作(图片上半部分是 16057,这里是 8261,说明进行了两次不同的实验)。
-
-
预期现象:目标进程的 CPU 使用率会瞬间飙升至接近 600%(如果是容器环境则看宿主机限制),导致应用无法及时处理业务逻辑,响应时间变长。
总结:实验目的与可观测性闭环
这一系列操作构建了一个完整的混沌工程实验闭环:
-
注入故障:通过
blade制造 OOM 和 CPU 满载。 -
监控验证:通过
jvisualvm和lsof确认故障对 JVM 和网络的实际影响。 -
容错验证:(虽然图中未展示,但这是最终目的)观察在发生上述故障时,系统的熔断、降级和自动扩缩容机制是否生效,从而保证核心业务不宕机。

当前展示的是针对 JVM 内部运行态进行攻击的高级混沌实验,主要涉及 CPU 满载 和 线程异常暴增 两个场景。以下是详细的命令解析及对分布式系统的潜在影响分析:
一、 核心实验命令解析
1. 模拟 JVM CPU 挂满
bash
[root@k8s-node1 ~]# blade create jvm cpufullload --cpu-count 6 --pid 8261
-
命令含义:向 PID 为
8261的 Java 进程注入 CPU 满载故障,强制占用 6 个 CPU 核心。 -
参数解析:
-
--cpu-count 6:指定需要占满的 CPU 核数。 -
--pid 8261:目标 Java 进程的 ID。
-
-
预期现象:该进程的 CPU 使用率将瞬间飙升至接近 600%(假设机器核数充足),导致应用无法及时处理正常的业务逻辑,接口响应时间(RT)显著拉长。
2. 模拟 Running 线程异常增多
bash
[root@k8s-node1 ~]# blade create jvm threadfull --thread-count 100 --running --pid 8261
-
命令含义:在目标进程中强制创建并运行 100 个线程。
-
参数解析:
-
--thread-count 100:指定创建的线程总数。 -
--running:最关键参数。它表示创建的线程不是处于休眠或等待状态,而是立即进入运行状态(RUNNABLE),疯狂抢占 CPU 时间片。 -
--pid 8261:目标进程 ID。
-
-
预期现象:操作系统上下文切换(Context Switching)开销剧增,CPU 忙于在不同线程间切换而实际做无用功,导致服务访问出现严重延迟。
二、 故障传导分析:从线程暴增到服务雪崩
在微服务架构中,强制注入大量 RUNNABLE 线程会引发致命的级联反应:
-
内核态资源耗尽:大量线程的创建会瞬间消耗进程的最大文件描述符限制(ulimit -n)和内存(线程栈 Thread Stack)。
-
CPU 调度风暴:当线程数超过物理核心数数倍时,操作系统的线程调度器会陷入瘫痪。原本毫秒级的业务处理会被拉长至秒级甚至分钟级。
-
连接池假死:
-
下游服务(如数据库、Redis)的响应变慢,导致当前服务的连接池(Connection Pool)中的连接长期处于占用状态无法释放。
-
新的请求到来时发现连接池已满,开始排队或等待。
-
随着请求堆积,Tomcat/Jetty 的工作线程池也会被占满,最终导致网关返回
503 Service Unavailable。
-
三、 可观测性排查闭环
结合您作为可观测性平台负责人的角色,针对此类“线程/CPU 类”故障,应指导分布式核心项目组按以下路径排查:
1. 指标层(Metrics):发现“调度风暴”
-
观测点:在 Prometheus 中监控
process_cpu_load和jvm_threads_live_threads。 -
预期现象:
-
jvm_threads_live_threads曲线会垂直拉升,表明线程数激增。 -
process_cpu_load接近 100%,且系统级指标node_cpu_seconds_total(iowait 或 steal 指标) 可能升高,表明 CPU 忙于无效调度。
-
2. 调用链层(Tracing):定位“长尾延迟”
-
观测点:登录 SkyWalking 或 Jaeger。
-
预期现象:
-
调用链拓扑图中,受影响服务的 P99 延迟会从几十毫秒变成“红色长条”。
-
点击具体的 Span,可以看到
thread.sleep或runnable状态异常,或者底层抛出SocketTimeoutException。
-
3. 日志层(Logging):捕捉“资源枯竭”
-
观测点:在 Loki/ELK 中检索该时段内的 ERROR 日志。
-
预期现象:可能会出现
OutOfMemoryError: unable to create new native thread(无法创建新线程)或too many open files(文件描述符耗尽)的错误,这是线程爆炸的直接证据。
四、 行动建议
-
验证“快速失败”机制:
此类实验暴露了系统在极端压力下的脆弱性。建议推动研发团队在所有 HTTP 客户端和 RPC 调用中配置合理的 Timeout(连接超时、读取超时)。在模拟故障场景下,验证服务是否能快速抛出异常并进行熔断,而不是无限期等待拖垮整个线程池。
-
建立“线程池隔离”规范:
建议分布式核心项目组审视核心服务的线程池配置。对于非核心接口,应考虑使用独立的线程池进行隔离,避免某个接口的异常(如死循环或外部超时)耗尽全局的工作线程。
posted on 2026-08-05 15:48 luzhouxiaoshuai 阅读(12) 评论(0) 收藏 举报
浙公网安备 33010602011771号