使用 IDEA 远程 Debug 调试
背景
在某些情况下,进行远程调试是一项必要的技能,在本研究中我们探讨了如何实现远程调试这一技术要点,并详细分析了使用IDEA软件执行远程调试时所涉及的关键步骤。通过阅读本文内容,读者将能够解答一些关于远程调试的问题。
配置
远程调试服务是一种常见的技术手段,在Spring Boot微服务架构中尤其常见。通常情况下,在配置环境中添加以下必要的组件即可实现功能:启动一个基于Spring Boot的微服务框架通常需要附加特定的启动参数
启动脚本改造
搭建一个基于Spring Boot的Web测试应用,并将其打包为JAR文件;随后部署至远程服务器;最后优化原有的启动配置文件。
使用第一步得到的 Command line arguments for remote JVM 即可,即
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=50055
改造后的启动脚本如下
nohup java \
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=50055 \
-jar demo.jar &
注意在windows中用 ^ 来进行换行,例如:
java ^
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=50055 ^
-jar demo.jar
说明:
可以选择任意可用端口,并非必须与IDEA远程设置的一致。需要注意的是,在启动远程连接时必须与IDEA远程配置中的设置保持一致。对于详细的操作说明,请参考附录内容或自行查阅相关资料。
2、demo.jar 改成给你自己的 jar 包名字
3、脚本是后台运行的,如不需要后台运行,自行去掉 nohup 和 &
3、启动远程服务器中的springboot项目


IDEA配置
不同版本的 IDEA 配置可能存在界面差异。
我采用的是 2021 年 2 月 1 日的那个版本。
大体类似,并且自己尝试过一些配置方案。
IDEA打开远程启动的springboot应用程序所对应的
1.选择 Edit Configuration

2.如图,点击加号,选择Remote

3.配置,详细步骤见图

注意:注意端口别被占用。后续这个端口是用来跟远程的java进程通信的。
可以注意到:切换不同的jdk版本,生成的脚本不一样
选择 jdk1.4,则为:
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=50055
这正是因为查阅其他博客会存在这种配置的原因。实际上这种配置也是可行的。但更准确地说应该是具体来说按照下面提到的jdk5-8的配置。
选择 jdk 5-8,则为:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=50055
建议采用JDK 9及以上版本。据说是因为JDK 9版本更加安全了,在进行远程调试时仅限本地环境。如果需要进行远程调试,则需在端口前配置相关参数。
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:50055
IDEA远程调解的细节
细节1:停在本地断点,关闭程序后会继续执行吗
当远程调试在断点处停止时,在关闭IDEA中的项目停止运行后,在这种情况下是否会继续执行剩余的逻辑?是的,在这种情况下会继续执行剩余的逻辑,并且这一点需要特别注意和记忆。
以如下代码为例,在第一行处陷入停滞。当我们在IDEA中触发停止时,发现即使在程序停止后,控制台仍会输出剩余的日志信息。

细节2:jar包代码和本地不一致会怎么样?
IDEA 里的代码如果不和jar包的一致,会怎么样。
结论:要保证和远程启动的代码一致。
在调试过程中,如果你无法对齐行数,则可能出现问题.但是错误处理通常会正常.类似情况仍然能够保持行数对齐.
当你调试时,在嵌套的方法中添加代码块不会干扰到父方法所在的行号范围;这种方法能够在调试过程中精确反映各行的编号。
细节3:日志打印在哪里?
日志不会以传统的方式显示在IDEA开发环境中使用的编辑器窗口中。具体来说,在这种配置下,System.out语句以及log.info方法仍然会将输出发送到远程服务器。
@GetMapping("/test1")
public String test1() {
System.out.println("第一行");
System.out.println("第二行");
log.info("log 第一行");
log.info("log 第二行");
return "ok";
}
细节4:调试时其他人会不会卡住?
远程调试的时候,打了断点,停住后会不会导致页面的请求卡住。
比如说,在进行远程调试时,请问其他QA人员正在测试该页面的情况是怎样的呢?会出现问题吗?我们之前也遇到过类似情况。
细节5:本地代码修复bug远程调用的时候
当我在远程调试过程中遇到一个bug时,在修改完成后重启该IDEA进程,并在页面再次访问该应用时询问是否能解决问题?这不会奏效,请确认该应用仍然基于远程部署的jar文件运行。
这个彻底粉碎了点击远程页面单点触发本地代码启动 debug的梦想。一实现此功能调试本地代码将变得异常便捷。
细节6:这个不算远程调试的问题,是dropframe的问题,放在这里一起讲了
关于frame drop的问题,在frame drop之后再次调试时是否会插入两条记录?
如图所示,在用户Mapper中调用insert(eo)操作。需要注意的是,默认情况下本方法未配置事务控制关键字 @Transactional。当mapper方法完成之后会立即提交事务,则会导致库表中新增一条记录。如果在drop frame之后重新调试并重新运行该代码时,则会再插入一条记录。

当启用 @Transational 事务隔离级别时,断开时事务未提交;再次运行此插入语句也不会产生两条记录。
关于什么是drop frame

细节7:跟上面一样,是dropframe问题
如果我们采用调用远程接口替代数据库插入逻辑,在dropframe之后重新运行相同的操作流程,并且这样做会不会导致远程接口被执行两次?答案是会。
总结
从感觉上来看,并不觉得远程调试的应用范围非常广泛;它无法作为一种长期使用的调试工具;相反地,在这种情况下它只能作为一个辅助手段使用
难点有几个:
1.难保证本地代码和远程一致,而且你也很难判断是否一致。
利用远程调试手段识别出了问题;然而却无法在修复后再继续进行后续调试;因此必须是在完成修复操作之后才能进行部署并持续执行远程调试
