当前位置: 首页 > java >正文

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和watchexec的区别

关键词: air原理 dlv原理 air + dlv debug

目标

  1. windows本地开发能够使用air热重载能力,同时支持页面debug 打断点调试
  2. 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客户端需要修改代码后手动重新启动

air和watchexec的区别

# 启动命令:用 air 实现热重载(需在容器内安装)
# command: air -c .air.toml
# 使用 watchexec 监听文件变化,触发编译和调试
command: >watchexec --watch . --ignore tmp/ --ignore vendor/ --ignore .git/ "go build -gcflags=\"all=-N -l\" -o ./tmp/main . && dlv exec --headless --listen=:2345 --api-version=2 --accept-multiclient ./tmp/main"

air 是针对go做的,日志中有变动信息,支持.air.toml配置文件进行配置

watchexec 更通用、稳定,日志信息较少(比如文件修改只提示有变动,不提醒是哪个文件变动),配置需要通过命令行添加

http://www.xdnf.cn/news/19487.html

相关文章:

  • Linux shell 脚本基础 003
  • C6.7:输入电阻的负载效应及其CE负反馈放大器
  • android-studio 安装
  • Mysql中事务隔离级别有哪些?
  • Java实习:MySQL篇(黑马JavaWeb课程)
  • 简单的加密算法
  • PostgreSQL表膨胀的危害与解决方案
  • 人工神经网络(ANN)深度学习
  • 开源 C++ QT Widget 开发(九)图表--仪表盘
  • dayjs ​JavaScript 时间日期处理库
  • P2P技术应用:去中心化
  • Java全栈开发面试实战:从基础到微服务的全面解析
  • NAS Docker 安装N8N
  • 鸿蒙ArkTS 核心篇-18-@Builder 自定义构建函数
  • 上海交大具身导航中的感知智能、社会智能和运动智能全面综述
  • 数值分析——非线性方程与方程组的数值解法之二分法
  • APB协议​​ 构建一个完整的 ​​UVM验证VIP Agent介绍类的要素
  • 壁纸、logo、短视频去水印
  • 前端常见安全问题 + 防御方法 + 面试回答
  • Qt QML连接数据库如何解决重复创建连接问题
  • 大话 IOT 技术(3) -- MQTT篇
  • Qt中使用 GStreamer 播放视频文件
  • HikariCP vs DBCP2 vs Tomcat JDBC:多场景数据库连接池方案对比与实践指南
  • 局域网中使用Nginx部署https前端和后端
  • Qt中解析XML文件
  • word中插入字符后会自动删除后面的字符
  • Visual Studio Code中launch.json的解析笔记
  • Prometheus之启用--web.enable-remote-write-receiver
  • 对于一个多层感知机,参数初始化的时候不是已经把权重的范围根据方差进行优化过了,为什么还要进行正则化惩罚过大权重
  • springboot整合minio实现上传下载搭建minio