microservices-book
  • 前言
  • 微服务基础
    • 微服务架构和单体应用架构
    • 微服务架构特征
    • 微服务治理与去中心化
    • 微服务演进式设计与优缺点
    • 微服务宏观把控
  • 面向服务的架构
    • SOA 理论与概念
    • 维基百科微服务
    • 微服务和 SOA 对比
  • Spring Boot
    • Spring Boot 应用起步与配置
    • Spring Boot 应用配置分析
    • Spring Boot 打包文件内容与结构
    • 使用 Gradle 构建 Spring Boot 应用
    • Spring Boot 参数自动装配
    • Jar 文件规范
    • Spring Boot Loader 源码分析
    • 反射扩展
    • JDWP 远程调试
    • 使用 JDWP 调试 Spring Boot Loader 源码
    • @SpringBootApplication
    • @SpringBootConfiguration
    • @Configuration
Powered by GitBook
On this page
  • 背景
  • 定义
  • 观察
  • suspend
  • transport
  • address
  • server
  • launch
  • onthrow
  • onuncaught
  • timeout
  • mutf8
  • quiet
  • 使用
  • 关联源代码
  • 启动服务端 socket
  • 启动客户端 socket
  • 注意

Was this helpful?

  1. Spring Boot

JDWP 远程调试

Previous反射扩展Next使用 JDWP 调试 Spring Boot Loader 源码

Last updated 5 years ago

Was this helpful?

背景

在日常开发中,大家借助 IDE 都会经历调试程序的情况,除 IDE 以外,其实 Java 还提供了一种调试程序的协议,也就是 JDWP。

典型的场景,代码在本机运行正常,发布到服务器后,就出现了各种问题。这个时候如果想定位问题位置,就变得不是那么简单了。一种是打日志,就需要把相关的日志都打印出来,还牵扯到重新部署等等问题,会非常麻烦。另外一种比较好的方式是远程调试,程序还是在服务器,把服务器的程序与本机源代码做关联,在本机可以根据断点单步调试来定位问题。

定义

Java Debug Wire Protocol, Java 调试协议。

无论以 main 方法运行,还是以 jar 包运行,都可以远程调试。本质来说,是通过 socket 和端口号把服务器和客户端连接。连接建立成功以后,就可以通过断点定位到需要调试的程序。

观察

打开命令行,输入 java 命令。

再次输入 agentlib 指令。

java -agentlib:jdwp=help

主要是前 4 个选项。

suspend

是否在启动的时候就等待,表示程序一启动就停下,等待远程调试 socket 和它建立连接。

transport

传输规范,用 JDWP 调试程序一般叫做:dt_socket。

address

地址,表示的是需要调试的地址。

server

是否监听调试器,需要改成 y,要监听调试器。

launch

当事件发生时运行调试器(用不到)。

onthrow

抛出异常时(用不到)。

onuncaught

没有捕获异常时(用不到)。

timeout

监听超时时间。(用不到)。

mutf8

(用不到)。

quiet

(用不到)。

使用

使用 JDWP 协议进行远程调试的时候,有两个 socket,一个是服务器,另一个是客户端。服务器优先启动,然后等待客户端来连接,客户端也称为调试器(debugger)。

关联源代码

首先把想要调试的源代码和 IDE 进行关联,并在 IDE 内打好断点。

题外话:直接在 idea 里面关联了 spring-boot-loader.jar 源码后,打好断点。右键 debug 来调试是无法进入 spring-boot-loader.jar 源码的,原因还是因为类加载器的问题。右键使用的是系统类加载器,并没有使用到 Spring Boot 提供的类加载器。

启动服务端 socket

监听调试器,启动好服务器端的 socket。

java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5050 -jar microservices-0.0.1-SNAPSHOT.jar

这里的 address 不用写 ip 地址,只需要些端口号就可以了。因为这里是启动服务器,只有客户端来连接服务器的时候,才需要指定服务器的 ip ,而服务器启动只需要指定端口就可以了。

当出现 Listening for transport ... 的时候就说明服务端的 socket 已经启动成功了,在本地 5050 这个端口号上等待客户端 socket 的连接。

启动客户端 socket

首先在 Idea 的 Run/Debug 里面找到 Remote 选项。

随便取一个名字。

Debugger mode 选择 Attach to remote JVM,表示附加到远程的 JVM 上。Transport 选择 Socket 方式,Host 为本机,Port 修改为 5050,module 选择我们自己的模块。

会发现下面出现了命令行指令

-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5050

address 没有 ip 的原因是调试本机 localhost 可以省略,连接远程的话就必须把 ip 写全。比对没什么错误后,点击 OK,就可以在 Idea 里面直接运行了,点击 debug 小虫虫。

神奇般的进入了之前在 JarLauncher 里面打的断点。

现在就可以用调试的方式,来逐步阅读 spring-boot-loader.jar 源码了,对于特别复杂的框架源码来说,实时的查看每一步执行的结果,这样的效率提升大大降低了阅读源码的困难性。

注意

以上是基于 jar 运行方式的远程调试,如果目标应用是 web 应用,那么需要针对 tomcat 做一些 JDWP 的配置,就可以远程调试 web 应用了。一旦客户端断开了调试,那么服务器也需要重新启动 socket。

cmd-java
cmd-java-agentlib-jdwp-help
jdwp-dt-socket
idea-run-remote
jdwp-remote-client
idea-run-remote-button
idea-debug-JarLaunher