【SkyWalking从入门到精通】第07篇:远程调试那些事——线上SkyWalking Agent的正确调试姿势
上一篇【第06篇】JavaAgent原理——SkyWalking无侵入探针的魔法秘密
下一篇【第08篇】Service Mesh入门——下一代微服务通信架构你了解多少
摘要
SkyWalking的探针插件出了问题,测试环境复现不了,生产环境又不能加日志重新发布——这是很多SkyWalking开发者和运维人员头疼的问题。这时候,远程调试(Remote Debug)就是你的救命稻草。本篇讲清楚JDWP协议原理、JVM远程调试参数配置、IDEA连接远程调试的完整步骤,以及如何安全地在测试环境中调试SkyWalking Agent代码。
一、远程调试是什么?
本地调试大家都熟悉:在IDE里打断点,启动Debug模式,代码执行到断点处就暂停,你可以查看变量值、单步执行。
远程调试的目标一样,只是被调试的程序运行在另一台机器(或者另一个JVM进程)上,你的IDE从本地发出调试指令,控制远端JVM的执行。
┌─────────────────────────────────────────────────────────────┐ │ 远程调试架构 │ │ │ │ 你的电脑(IDEA) 服务器 │ │ ┌──────────────────┐ TCP连接 ┌──────────────────┐ │ │ │ │ ←────────→ │ │ │ │ │ IDEA Debugger │ │ 目标JVM进程 │ │ │ │ │ │ + SkyWalking │ │ │ │ 打断点、查变量、 │ │ Agent │ │ │ │ 单步执行 │ │ │ │ │ └──────────────────┘ └──────────────────┘ │ │ │ │ 调试指令通过JDWP协议传输 │ └─────────────────────────────────────────────────────────────┘二、JDWP协议:Java调试的基础设施
远程调试能工作,依赖于JVM内置支持的**JDWP(Java Debug Wire Protocol)**协议。
JDWP是Java平台调试体系(JPDA)的一部分,它定义了调试器(如IDEA)与被调试JVM之间的通信协议,包括:
- 设置/清除断点
- 暂停/继续线程执行
- 查询变量值
- 获取调用栈
当你在JVM启动参数里加上特定参数,就开启了JDWP监听,调试器就可以通过网络连接过来控制JVM了。
三、开启远程调试的JVM参数
基本格式
# Java 5-8版本的写法(旧式)java-Xdebug-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005\-jaryour-app.jar# Java 9+版本的推荐写法(新式,更简洁)java-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005\-jaryour-app.jar参数详解
| 参数 | 含义 | 常用值 |
|---|---|---|
transport | 传输方式 | dt_socket(TCP/IP,最常用) |
server | 是否以服务模式等待连接 | y(被调试端等待调试器连接) |
suspend | 是否在启动时挂起等待调试器 | n(正常启动,不等待)/y(等调试器连接后才继续) |
address | 监听地址和端口 | 5005(端口号)/*:5005(Java9+,接受所有IP) |
suspend=yvssuspend=n的使用场景:
suspend=n:应用正常启动,调试器随时可以连入,适合已运行的服务suspend=y:应用启动后立即挂起,等待调试器连接后才继续执行,适合调试启动过程(比如Agent的premain方法)
四、调试SkyWalking Agent代码
SkyWalking Agent在应用启动时就开始工作(premain方法),调试Agent代码有其特殊之处。
场景1:调试Agent插件运行时逻辑
如果你想调试某个插件的拦截器(比如Spring MVC插件),可以这样设置:
# 启动你的Spring Boot应用,同时加载SkyWalking Agentjava\-javaagent:/path/to/skywalking-agent/skywalking-agent.jar\-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005\-jaryour-spring-boot-app.jar注意:-javaagent和-agentlib:jdwp两个参数可以同时存在,互不干扰。
场景2:调试Agent启动过程(premain方法)
如果你想调试Agent初始化过程,需要suspend=y:
java\-javaagent:/path/to/skywalking-agent/skywalking-agent.jar\-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005\-jaryour-spring-boot-app.jar# 应用会挂起,等待你的IDEA连接上来后才继续执行五、IDEA配置Remote Debug
步骤1:创建Remote调试配置
- 打开Run → Edit Configurations
- 点击+号,选择Remote JVM Debug
- 配置如下:
Name: SkyWalking Agent Debug Host: localhost(或者服务器IP) Port: 5005IDEA会自动生成对应的JVM启动参数,复制到你的应用启动命令里。
步骤2:导入SkyWalking源码
要在Agent代码里打断点,你需要让IDEA知道源码位置:
# 方式1:直接克隆SkyWalking源码仓库(推荐)gitclone https://github.com/apache/skywalking.gitcdskywalkinggitcheckout v8.x.x# 切换到与你部署版本对应的Tagmvn compile-DskipTests# 编译(不跑测试,节省时间)# 方式2:在Maven依赖里添加sources# SkyWalking的源码包可以在Maven中央仓库下载将源码项目在IDEA中打开,或者在Remote调试时,IDEA会自动匹配classpath中的class与源码。
步骤3:打断点并连接
在SkyWalking源码中的关键位置打断点,例如:
// 在插件拦截器里打断点,看每次HTTP请求的追踪过程// 位置:apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/...publicclassAbstractSpringMVCHandlerMethodInterceptor{publicvoidbeforeMethod(...){// 在这里打断点 ← 断点}}然后在IDEA中点击Debug 按钮(绿色虫子图标)连接到远程JVM。
当应用处理一次HTTP请求时,执行到你的断点处,IDE就会暂停,你可以查看:
contextCarrier里的TraceId是什么request请求的Header里是否有上游的追踪上下文span对象的完整属性
六、在Docker/Kubernetes环境中的远程调试
现在很多应用运行在容器里,远程调试需要额外处理端口映射。
Docker方式
# Dockerfile中的JVM启动命令 ENV JAVA_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" # 运行容器时映射调试端口 docker run -p 5005:5005 -p 8080:8080 your-app-image# 本地IDEA连接到 localhost:5005 即可Kubernetes方式(kubectl port-forward)
# 找到运行中的Podkubectl get pods-nyour-namespace# 临时将Pod的5005端口转发到本地kubectl port-forward pod/your-app-pod-name5005:5005-nyour-namespace# 这个命令运行期间,IDEA连接localhost:5005就能调试Pod内的应用七、生产环境的注意事项
⚠️远程调试在生产环境是非常危险的操作,必须遵守以下原则:
1. 永远不要在生产环境开启调试端口
调试端口一旦暴露:
- 任何能访问该端口的人都可以暂停JVM执行(制造服务停止)
- 可以读取JVM内存中的所有变量(包括密码、密钥)
- 等同于给攻击者提供了完整的服务器控制权
2. 只在测试/预发环境使用
正确流程:
1. 在测试环境复现问题 2. 启动远程调试 3. 调试分析根因 4. 修复后关闭调试端口 5. 部署到生产3. 调试完毕立即关闭
即使在测试环境,调试完毕也应立即重启应用,移除调试参数,防止误操作。
4. 使用防火墙限制访问
如果确实需要在内网服务器上开启调试,一定要通过防火墙限制只有特定IP可以访问调试端口:
# 只允许特定IP(开发机器)访问5005端口iptables-AINPUT-ptcp--dport5005-s192.168.1.100-jACCEPT iptables-AINPUT-ptcp--dport5005-jDROP八、快速排查Agent问题的其他手段
当远程调试不方便时,还有几种快速定位Agent问题的方法:
方法1:开启Agent日志
# agent.config中设置日志级别 logging.level=DEBUG logging.file_name=skywalking-api.log调试模式下,Agent会输出详细的插件加载、Span创建等日志。
方法2:使用官方的探针测试框架
SkyWalking官方提供了Plugin Test框架,可以在本地JUnit测试中运行探针,不需要启动真实应用:
// 插件单元测试示例@RunWith(PowerMockRunner.class)publicclassSpringMVCInterceptorTest{@TestpublicvoidtestBeforeMethod()throwsThrowable{// 模拟HTTP请求环境// 验证Span是否被正确创建// 验证TraceId是否正确传播}}方法3:使用skywalking-eyes工具
SkyWalking项目还提供了skywalking-eyes工具,可以扫描探针行为,验证插件是否正常工作。
本篇小结
Java远程调试基于JDWP协议,通过在JVM启动参数中加入调试代理,使得IDE可以通过网络控制远端JVM执行:
- 关键参数:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 - suspend=y:适合调试启动过程(Agent的premain阶段)
- IDEA配置:创建Remote JVM Debug配置,连接对应IP:Port
- Kubernetes环境:使用
kubectl port-forward转发调试端口 - 生产环境:永远不要开启,只在测试/预发环境使用
下一篇我们转换视角,介绍Service Mesh——SkyWalking监控体系的另一个重要战场。
上一篇【第06篇】JavaAgent原理——SkyWalking无侵入探针的魔法秘密
下一篇【第08篇】Service Mesh入门——下一代微服务通信架构你了解多少