引言
在之前的开发当中,都会进行本地项目启动,然后向本地服务发起请求来进行 Debug 调试代码,这也是开发人员最常见的调试操作。但是当项目逐渐成型,慢慢的将各个模块部署到服务器后,调试的手段可能就仅仅剩下查看执行日志了。然而和本地 Debug 代码不同,通过日志排查问题是非常不方便的,你需要在特定的位置打印日志预测问题可能出现的原因,无法精确、快速的定位问题。
为了解决这个问题,JVM 提供了远程调试的手段。我们在Linux 服务器上启动项目的时候,为其指定一系列jvm 参数,就可以让java 服务监听指定端口,并与本地代码进行 debug 通信,但一定要保证本地和服务器上的代码要保持一致。
另外,不是说远程 Debug 只能用Eclipse ,也并不是说项目类型必须是 Spring Boot 。远程调试的本质是 JVM 之间的 Socket 通信,因此,只要是 Java 项目就都可以进行类似的远程调试操作,只不过启动参数可能书写的位置不同罢了,而且 IDEA 也是有类似远程调试的功能的,本篇博客的灵感就来自 IDEA 远程调试的视频教程:《DEBUG技巧-远程调试》
一、项目启动参数
目前主流的 Spring Boot 项目都是通过 jar 文件进行启动的,例如这样:
nohup java -jar demo.jar &
那么如果想进行远程调试,启动参数就必须加上一些允许远程调试的配置参数:
nohup java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000 -jar demo.jar &
其中:
-Xdebug 代表开启debug 模式,允许远程debug 数据交互
-Xrunjdwp 代表Java Debug Wire Protocol ,Java Debug 电报协议
transport=dt_socket 表示以 Socket 方式进行数据传输
server=y 表示启动的虚拟机是调试的服务器端
suspend=n 表示不挂起虚拟机,默认是 y ,本参数表示在客户端建立连接前,服务端是否挂起。n 代表不挂起,即不论我们的客户机是否进行debug 连接都不必等待直接启动。
address=8000 表示服务端监听的客户机 debug 端口
可以看到,即便是最必要的远程调试参数也是长长的一串,所以,建议将启动命令以Shell 脚本的方式存储,如下所示:
app.sh 脚本文件:
二、启动项目
执行配置好远程 debug 参数的 java -jar 命令后,我们可以通过检查网络状态来观察对应的端口监听情况:
使用命令 :
netstat -anp | grep java
目前我只有一个 Java 项目在 CentOS 6 (ip :192.168.1.140)上运行,可以看到两个醒目的 LISTEN ,代表 8000端口和 8080 端口都已经被 Java 进程监听。注意,在设置debug 参数的时候我们设置 suspend=n,因此,此时如果不进行 debug 调试的话,服务没有挂起,因此不会有任何影响,依然可以正常工作。
三、Eclipse 连接远程服务
启动好了服务器上的应用程序,且8000 端口已经被监听,那么我们只需要在 自己的本机配置远程调试 IP 和 端口 即可。注意,不论你使用的是不是 Eclipse ,都是有对应的配置的。如果你的远程主机需要通过跳板机访问,需要另外配置端口转发,在文末的注意事项中会提到。
打开 Debug Configuration:
右键弹出浮窗,选择 New Configuration:
填写必要的连接信息:
点击 Apply ——> Debug,可以观察到Eclipse 右下角的提示信息:
虽然这里的进度条信息是 Launching ,但实际上并不代表启动远程服务的意思,而是代表启动本地的客户机代码并连接到服务端。连接成功后,Eclipse 界面不会有任何变化,也不会跳转到 Debug 界面。接下来我们进行一下验证。
四、远程调试验证
又到了验证环节,刚刚有提到,连接成功后不会有任何提示和变化,那我们如何知道是否连接成功了呢?
首先你可以试着再次连接,那么就会提示下面的弹框:
如果你是人生第一次进行远程调试,很可能就被这个对话框唬住了,以为自己连接失败了,我就是这么被骗了好半天。
弹出此对话框的原因是我们已经连接成功了,因此,后面的其他连接都会提示拒绝。
如果将 Eclipse 切换到Debug 界面,可以看到“小红灯” 和 Disconnect 按钮都是红色亮起的状态,这是第一个可以直观验证连接成功的途径:
第二个验证的途径是返回远程服务器,再次查看网络监听状态:
可以清晰的看到,8000 端口已经由 LISTEN 变为了 ESTABLISHED,由于我的Linux 是放在 VMware 虚拟机上,因此这里显示的 192.168.1.1 是 NAT 的虚拟网卡,详情可以参考我的《Linux进阶之路————CentOS网络配置》
第三个验证的途径就是最终我们想要的效果。
我们打开浏览器,请求服务器的资源 url ,可以看到我们的本地 断点成功拦截:
可以看到,我们通过浏览器访问的服务器资源地址,被本地的断点成功拦截到了请求。
五、注意事项和补充说明
需要说明的是,服务器上的jar 文件代码必须要和本地保持一致,否则可能出现调试混乱的情况。
如果服务器开启了防火墙,那么需要将我们远程调试的 Debug 端口放行,具体做法可以参考我的《Linux实操———CentOS 6 安装配置 Tomcat》第三节,简单来说就是修改 /etc/sysconfig/iptables 文件,照着 22 端口的样子粘贴即可,然后重启防火墙。
另外,还有一点需要说明的是,如果在企业中远程服务器无法直接通信,即可能需要跳板机或者堡垒机访问,那么可以配置 Debug 端口的端口转发,可以思考类似的场景:本地的SQLyog 如何连接到受跳板机保护服务器上的MySQL,并参考我的《服务端开发——云服务器的端口转发设置(SSH隧道)》
注意,远程Debug模式可以成功将访问服务器的请求拦截到本地代码的断点上,此时本地和远程的代码都处于暂停(同步执行)的状态,即远程服务会等待开发者执行 Debug 操作,如 Resume(F8)、Step Over(F6) 等。如果希望退出Debug,只需要点击 Disconnect 按钮断开通信即可,点击后,远程服务自动向下继续执行。
注意,一定不要点击Terminate按钮,如果只是希望退出 Debug 而误点了“小红灯”那么远程服务也会被终止,需要再次重启服务才能恢复。
其实只要断开连接,远程服务和本地的Eclipse 不会产生任何通信,所有的断点都不会生效。而一旦连接成功,那么所有本地Debug操作都会被传送到远程服务,因此,一定要谨慎操作,避免在不必要的时候使远程服务停滞或关闭!
远程Debug 可以反复断开、重连。
参考文章
《JDWP了解》
《微服务学习(一)-使用Springboot配合Eclipse远程调试》
《eclipse远程调试之springboot》
综上,是关于远程调试的一些知识总结,欢迎文末留言。