unix domain socket 本地 ipc 需确保路径为绝对路径、父目录有执行权限、启动前用 os.removeall 清理旧 socket 文件,客户端应重试连接,优先选用 sock_stream 模式并自定义帧头,gin/http.server 使用时须正确管理 listener 生命周期和 shutdown。

用 net.Listen("unix", path) 做本地高效 IPC
Unix Domain Socket(UDS)是同一台机器上进程通信的首选,性能远超 TCP loopback,且天然支持文件权限控制。关键不是“能不能用”,而是路径、权限和清理这三件事必须做对。
常见错误现象:connect: connection refused(服务端没起来或 socket 文件残留)、permission denied(路径所在目录无执行权限)、address already in use(旧 socket 文件未删干净)。
- 服务端启动前务必调用
os.RemoveAll(socketPath)清理旧文件,不能只靠os.Remove—— 如果是目录会失败 - socket 文件路径必须是绝对路径,且其**父目录要有可执行(
x)权限**,否则客户端连不上(Linux/Unix 下路径遍历需要 x 权限) - 监听后不要立即
defer listener.Close(),否则循环 accept 前就关了;应 defer 在 for 循环外,或用defer func() { _ = listener.Close() }() - 客户端连接失败时,别直接 panic,建议加重试逻辑(比如 3 次,每次 sleep 100ms),因为服务端可能还没完成 listen
os.Pipe() 只适用于父子进程,不跨进程
很多人误以为 os.Pipe() 能用于任意两个 Go 进程通信,其实它返回的是内存中的一对关联 io.ReadCloser/io.WriteCloser,本质是内核管道描述符,**只能在 fork 后的父子进程中传递**。独立运行的两个 go run 程序之间无法共享这个 pipe。
如果你看到 “broken pipe” 或读写立刻 EOF,大概率是把 pipe 当成了命名管道(named pipe / FIFO)来用。
- 真正跨进程的“命名管道”要用系统级命令创建:
mkfifo /tmp/myfifo,再用os.OpenFile("/tmp/myfifo", os.O_RDONLY, 0)和os.OpenFile("/tmp/myfifo", os.O_WRONLY, 0)分别打开 - FIFO 是阻塞式:一端 open 时,若另一端未 open,就会卡住;所以 reader 和 writer 必须并发启动,或加超时控制
- FIFO 不支持 seek,不能随机读写;也不支持双向通信,得开两个 FIFO(或改用 UDS)
选 SOCK_STREAM 还是 SOCK_DGRAM?优先用流式
Go 的 net.Listen("unix", ...) 默认走 SOCK_STREAM(可靠、有序、面向连接),对应 TCP 行为;而 SOCK_DGRAM(类似 UDP)需显式指定,用 net.ListenUnixgram,但实际极少用。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
除非你有明确需求——比如发一堆短小、独立、允许丢失的消息(如监控打点),否则别碰 datagram 模式。它在 Go 中缺乏高层封装,容易踩坑:
-
net.ListenUnixgram返回的是*net.UnixConn,没有Accept,只能ReadFromUnix/WriteToUnix,无法维持长连接语义 - 消息大小受内核限制(通常 65536 字节),超长会被截断,且无重传机制
- Go 标准库不提供
SOCK_SEQPACKET(有序数据包),该类型在 Linux 上支持原子消息边界,但 Go 未暴露 - 绝大多数业务场景(API 交互、配置同步、日志转发)都更适合
SOCK_STREAM+ 自定义帧头(如 length-prefix)
Gin 或 http.Server 复用 UDS 时注意 Listener 生命周期
把 Gin 或 http.Server 绑定到 Unix socket 上很常见,但容易忽略 listener 的关闭时机和错误处理。
典型问题:服务重启时,新进程启动失败,报 listen unix /tmp/app.sock: bind: address already in use,但老进程已退出——其实是 socket 文件残留,而非端口被占。
- 启动前清 socket 文件是必须步骤,且要检查
os.IsNotExist(err),避免误删其他文件 - 用
http.Server.Serve(listener)启动后,listener.Close()不会自动触发 shutdown,必须手动调用srv.Shutdown()并等待完成 - 如果同时跑 HTTP(TCP)和 UDS 两个 listener,别共用同一个
http.ServeMux却忘了路由隔离:UDS 请求的Request.URL.Path是有效的,但Host为空,某些中间件可能 panic - 调试时用
curl --unix-socket /tmp/app.sock http://localhost/ping最方便,不用改 client 代码
Unix socket 的路径权限、残留清理和阻塞行为,比看上去更敏感。很多“连不上”的问题,根源不在代码逻辑,而在文件系统那一层是否真的就绪。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










