查当前进程 fd 使用量需执行 ls -l /proc/$(pidof your-binary)/fd/ 2>/dev/null | wc -l 获取总数,并用 grep socket 检查是否 socket 占满;若超 ulimit -n 的 80% 需干预;go 程序须在 main() 中用 syscall.setrlimit 主动设限,注意权限与系统限制;http.server 必须配置 maxconns 等超时参数防 fd 耗尽。

查当前进程 fd 使用量,别等报错才动手
“too many open files”不是预警,是熔断信号。此时服务已拒接新连接,必须立刻查 /proc/<pid>/fd/</pid> —— 不依赖 lsof(生产环境常未安装)。执行:ls -l /proc/$(pidof your-binary)/fd/ 2>/dev/null | wc -l 得到粗略总数;再用 ls -l /proc/$(pidof your-binary)/fd/ 2>/dev/null | grep socket | head -10 看是否大量 socket:[...],确认是否被连接占满。
注意:结果含符号链接,实际 fd 数略低,但趋势可靠。若数值 > 80% 的 ulimit -n 值(如 1048576 × 0.8 ≈ 838860),说明已逼近硬限,需干预。
用 syscall.Setrlimit 在启动时调高上限,但权限必须到位
Go 程序不能靠 shell 的 ulimit -n 临时设置生效——systemd 服务、容器或非交互式启动时该设置会被忽略。必须在 main() 开头用 syscall.Setrlimit 主动申请:
先调 syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit) 获取当前软硬限;
再设 rLimit.Cur = 1048576(软限),rLimit.Max = 1048576(硬限);
最后 syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit) 提交。
常见失败原因:
• operation not permitted:进程没 CAP_SYS_RESOURCE 能力,容器需加 --cap-add=SYS_RESOURCE;
• invalid argument:设的值超过系统全局上限(/proc/sys/fs/nr_open),需先调高该值;
• systemd 服务必须在 unit 文件里显式加 LimitNOFILE=1048576,否则 Go 层调用会静默失败。
http.Server 必须配 MaxConns,否则 FD 耗尽只是时间问题
http.ListenAndServe 默认不限连接数,每个新连接启一个 goroutine,FD 消耗完全失控。Go 1.19+ 必须用自定义 http.Server 并设 MaxConns:MaxConns: 50000 是硬闸,超出连接由内核 SYN DROP,不进应用层;IdleTimeout: 30 * time.Second 防长连接空占 FD,HTTP/2 或 WebSocket 场景建议压到 15s;ReadTimeout 和 WriteTimeout 防慢请求拖住 FD。
旧版 Go(MaxConns,得自己 wrap net.Listener,用 sync.Semaphore 或原子计数器控制 accept;别信 “goroutine 天然轻量”,FD 是内核资源,不是 Go 运行时能调度的。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
监控 fd 泄漏,重点盯 os.File 和 http.Client
fd 泄漏不是并发高导致的,是某个 os.File 打开后没关,或 http.Client 连接池没复用。排查要点:
• 查 /proc/<pid>/fd/</pid> 中高频重复路径(如 /tmp/xxx.log、/var/log/app.log),对应代码必有 os.Open 未配 defer f.Close();
• 若出现大量 anon_inode:[eventpoll],说明 fsnotify.Watcher 或 net/http.Transport 实例未 Close;
• defer f.Close() 不等于安全:函数 panic 后被 recover、goroutine 永久挂起、循环变量闭包误用,都会让 defer 不触发;
• http.Client 必须复用,并设 Transport.MaxIdleConns 和 MaxIdleConnsPerHost,否则每个请求新建连接,fd 泄漏速度远超连接建立速度。
真正卡住人的从来不是“怎么设上限”,而是“哪个地方悄悄开了 fd 却没关”。查 /proc/pid/fd 是起点,pprof/goroutine?debug=2 搜 os.File 阻塞点是终点,中间差的是一行 Close() 调用和一次对 defer 生效边界的确认。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










