go 开发环境配置 air + dlv debug 踩坑之旅
文章目录
- 目标
- 面对问题
- 项目支持air 热重载
- windows .air.toml注意事项
- air启动热加载效果
- dlv
- dlv 下载
- dlv和goland debug GUI面板的关系
- dlv debug、attach、exec的区别
- 第一次尝试:air + dlv attach
- 步骤一:配置.air.toml并启动air
- 步骤二:dlv attach配置
- 演示
- 发现问题:当修改代码,热重载后,dlv 失效
- air热重载原理
- 第二次尝试:dlv attach页面操作调整为命令行
- 步骤一:启动air,终端输入dlv 命令
- 步骤二:添加go remote,启动运行
- 开启go remote的目的
- go remote常见使用场景
- 第二次尝试发现问题:与第一次相同
- dlv 终端问题
- 演示
- 第三次尝试:脚本控制自动监听进程切换
- dlv_air_watcher.sh脚本
- 演示
- 使用脚本而非air full_bin配置原因:windows下语法报错
- 问题
- 第四次尝试:换成虚拟机centos7远程开发
- .air.toml配置
- 虚拟机端口开放
- 注意: web服务需要运行在0.0.0.0:8080上而非127.0.0.1:8080上
- 防火墙允许8080端口通过
- goland remote开发演示
- 问题
关键词:
air原理
dlv原理
air + dlv debug
目标
- windows本地开发能够使用air热重载能力,同时支持页面debug 打断点调试
- centos7支持远程开发,能够使用air热重载能力,同时支持页面debug 打断点调试
面对问题
不使用air热重载,能debug;使用air热重载,不能用debug了。
关联issue: 这个工具不支持debug吗 · Issue #523 · air-verse/air
项目支持air 热重载
# 下载依赖
go install github.com/cosmtrek/air@latest# 验证安装
air -v# 生成.air.toml配置文件
air init# 启动air
air
windows .air.toml注意事项
air启动热加载效果
dlv
dlv 下载
# 下载dlv
go install github.com/go-delve/delve/cmd/dlv@latest
dlv和goland debug GUI面板的关系
goland IDE调用dlv作为后端,通过 debug GUI面板 图形化的封装了dlv的命令;如点击“断点”按钮对应这dlv break命令。
在golang 中点击“调试”按钮,IDE会自动:
-
用go build生成带调试信息的可执行文件
# 编译当前项目并生成带调试信息的可执行文件 go build -gcflags='all=-N -l' -o myapp
go build: 将源代码编译成可执行文件(构建)
-gcflags: 用于向GO编译器(gc)传递参数的标志
all: 表示对所有包应用后续参数(如果不加all,默认只对主包生效)
-N: 表示禁用编译器优化(N:no optimizations)。编译器优化可能会重排、合并或删除代码,禁用后能保证调试时看到的代码执行流程与源代码一致
-l: 表示禁用内联优化(l: no inlining)。内联会将函数调用直接嵌入到调用处,禁用后调试时能正常步进(step into)函数内部
-o: 指定输出文件的路径。编译后生成名为 “myapp” 的可执行文件(Windows 下为 myapp.exe)
-
调用dlv debug或dlv attach启动调试
-
将dlv返回的调试数据(断点状态等)展示在图形化界面上
dlv debug、attach、exec的区别
dlv attach: 连接到一个正在运行的进程进行调试,不会重新启动程序。适用于生产环境中出现的问题。
dlv attach PID
dlv attach myapp
dlv debug: 启动程序进入调试
dlv debug ./main.go # 调试指定的go文件
dlv debug ./cmd/myapp # 调试指定包
dlv exec: 调试已编译好的可执行文件(如main.exe)
dlv debug = go build(编译构建) + dlv exec
第一次尝试:air + dlv attach
步骤一:配置.air.toml并启动air
# .air.toml 文件修改配置
cmd = "go build -gcflags='all=-N -l' -o ./tmp/main.exe ."# 启动air
air
步骤二:dlv attach配置
查看项目运行端口,按照下面两张图操作,dlv attach到air启动的程序中。
# 查看项目运行端口
netstat -ano | findstr :8080
演示
发现问题:当修改代码,热重载后,dlv 失效
当修改代码后,air就会进行热重载,热重载后服务的PID就改变了,原来的dlv attach就失效了(因为dlv attach是通过PID绑定到特定进程的),需要重新配置dlv attach。
这意味着dlv attach是一次性的,每次修改代码后需要重新配置
air热重载原理
原来我本地开发就是运行项目,改了代码,然后重新运行(关闭原来的程序)。加了热重载就是帮我做了这件事:监听文件变化,关闭旧进程,开启新进程(重新运行)。
PID(进程ID)是操作系统为每个运行中的进程分配的唯一标识,每个新的进程PID都是唯一且不同的。// 所以热重载后服务的PID就改变了
关闭旧进程,开启新进程:go语言是静态编译的,不像javascript动态编译可以支持在原进程内部动态更新代码。
第二次尝试:dlv attach页面操作调整为命令行
页面操作操作调整成命令行,减少交互点击步骤,为第三次尝试 脚本操作 做铺垫。
步骤一:启动air,终端输入dlv 命令
# 终端启动air
air# 另一个终端查看项目运行端口
netstat -ano | findstr :8080
# 输入dlv命令
dlv attach 12345 --headless --listen=:2345 --api-version=2 # 12345 是进程 PID
步骤二:添加go remote,启动运行
添加go remote,启动运行操作如图
开启go remote的目的
开启远程调试后,delve会以“服务端”模式运行(–headless)并监听指定端口(–listen=:2345),即使被调试的进程重启,调试工具仍能保持服务状态,等待新进程连接或重新附加。
–api-version=2表示仅通过API接口接收调试指令 =》 确保IDE、脚本等工具通过统一协议(json-rpc)与dlv通信。
go remote常见使用场景
- IDE集成(自动):golang debug面板调用dlv逻辑:当点击golang的调试按钮(小虫子)时,IDE会自动后台启动dlv服务端(默认使用2345端口),并自动作为客户端连接该端口 // IDE debug GUI底层也是使用的go remote模式
- 容器/远程环境调试:如果应用运行在dokcer容器或远程服务器中,本地dlv无法直接attach到容器内的进程,必须通过–listen=0.0.0.0:2345暴露调试端口,再从本地连接远程调试服务
- 多工具协作:远程模式允许多个客户端(GUI、命令行)临时连接或断开,不影响调试服务本身
第二次尝试发现问题:与第一次相同
通过第一次尝试到第二次尝试,了解了air、dlv attach相关的部分原理。第二次尝试等价于第一次尝试,唯一变化是由GUI操作,调整为命令行操作。
当前问题与第一次相同:代码变动保存后,air kill旧进程,创建新进程;dlv attach的旧进程关闭后,随之关闭。
dlv 终端问题
dlv 终端不能ctrl c退出;退出air dlv终端结束但不可操作;
dlv终端会在attach进程关闭时自动退出;或者手动关闭终端实例时退出;
演示
第三次尝试:脚本控制自动监听进程切换
思路:dlv attach是一次性的,不支持自动监听进程切换。尝试写脚本支持监听进程切换后重新dlv attach
dlv_air_watcher.sh脚本
#!/bin/bash
echo "[DEBUG] full_bin started at $(date)" # 标记脚本开始执行# 配置参数
PROCESS_NAME="main" # 你的Go程序名(不带.exe)
DLV_PATH="/e/goland/go/bin/dlv.exe" # dlv.exe路径(Git Bash格式)
LISTEN_PORT=2345 # 调试端口
CHECK_INTERVAL=2 # 检查间隔(秒)# 检查dlv.exe是否存在
if [ ! -f "$DLV_PATH" ]; thenecho "错误:找不到 dlv.exe,请检查路径是否正确。"exit 1
fiecho "开始监听进程 $PROCESS_NAME,按 Ctrl+C 退出..."LAST_PID="" # 记录上一次检测到的PIDwhile true; do# 获取当前进程PID(使用 tasklist)CURRENT_PID=$(tasklist | grep -i "${PROCESS_NAME}.exe" | awk '{print $2}')if [ -n "$CURRENT_PID" ]; thenif [ "$CURRENT_PID" != "$LAST_PID" ]; then# 如果PID发生变化(新进程启动)if [ -n "$LAST_PID" ]; thenecho "检测到进程变化(旧PID: $LAST_PID → 新PID: $CURRENT_PID),终止旧调试会话..."kill $DLV_PID 2>/dev/null # 终止之前的dlv会话fiecho "发现新进程 $PROCESS_NAME (PID: $CURRENT_PID),附加调试器..."# 启动新的dlv attach"$DLV_PATH" attach $CURRENT_PID \--headless \--listen=:$LISTEN_PORT \--continue \--accept-multiclient \--log &DLV_PID=$!LAST_PID=$CURRENT_PIDfielseif [ -n "$LAST_PID" ]; thenecho "进程 $PROCESS_NAME 已退出,终止调试会话..."kill $DLV_PID 2>/dev/nullLAST_PID=""fiecho "未找到进程 $PROCESS_NAME,等待 ${CHECK_INTERVAL}秒后重试..."fisleep $CHECK_INTERVAL
done
演示
使用脚本而非air full_bin配置原因:windows下语法报错
问题
脚本可以监听任务重启后绑定dlv服务端,但goland客户端需要修改代码后手动重新启动
第四次尝试:换成虚拟机centos7远程开发
.air.toml配置
root = "."
tmp_dir = "tmp"[build]
args_bin = []
bin = "./tmp/main"
cmd = "go build -gcflags 'all=-N -l' -o ./tmp/main ."
full_bin = "dlv --listen=:2345 --headless=true --api-version=2 --check-go-version=false --continue --accept-multiclient exec ./tmp/main --"
delay = 1000
include_dir = []
include_ext = ["go", "tpl", "src"]
include_file = []
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"[log]
# Show log time
time = true
# Only show main log (silences watcher, build, runner)
main_only = true
# silence all logs produced by air
silent = false[misc]
clean_on_exit = false[screen]
clear_on_rebuild = false
keep_scroll = true
虚拟机端口开放
注意: web服务需要运行在0.0.0.0:8080上而非127.0.0.1:8080上
// 127.0.0.1:8080 本地环回地址只能自己访问
// 0.0.0.0:8080 监听所有网卡的请求
err := s.Start("0.0.0.0:8080")
防火墙允许8080端口通过
# 永久开放 8080 端口
firewall-cmd --add-port=8080/tcp --permanent
# 重新加载防火墙规则(永久配置需 reload 才生效)
firewall-cmd --reload
# 验证永久配置(输出 yes 表示成功)
firewall-cmd --query-port=8080/tcp --permanent
goland remote开发演示
goland remote开发演示
问题
同第三次尝试:goland客户端需要修改代码后手动重新启动