Golang socket 里面奇怪的 pipe 使用
有个 golang 写的 proxy server, 大概日常 tcp 连接数两万多左右,结果某次在 /proc/pid/fd 下面一看,竟然有五到六万多文件,ls -l 一看,居然 socket 只有两万多,pipe 反而有三万多小四万,这就奇了怪了,哪儿来的这么多 pipe?
读了一阵子源码搞清楚了,golang 在 Linux 下面,对于 fd 对拷这种操作【具体是 net.Conn.readFrom(net.Conn)】,有个用 splice 系统调用的优化,会先试着用 splice 调用在内核里面完成两个 fd 之间的拷贝,这个可以省去将大量数据从 kernel 里面拷贝到 user space 的操作。如果失败才正常拷贝。
然而 splice 这个调用可能是为了内部实现方便,目标一方必须是 pipe 类型的 fd。所以 golang 里面的实现其实比较龌龊,先创建一对 pipe fd,然后从源 fd splice 进 pipe 入端,接下来从 pipe 出端 splice 到目标 fd。 这样下来一个 readFrom 操作,里面要一次 pipe2 调用,2 次 splice 调用,还有 2 次 close 操作关掉 pipe。有趣的是,即使这么龌龊的实现,在数据量大的应用,比如 proxy 上面,依然可以达到比直接拷贝更好的性能,可以参考下面的第一个链接。
唯一感觉比较不爽的也就是每次 readFrom 都要创建 pipe 再销毁了,这个为了不影响 golang 的架构设计,可能也只能这么做。不过一般 proxy 里面最后大多是 io.Copy ,而这个调用一旦调用就直到 tcp 连接断开了,生命周期不会太短,所以完全没有必要再做任何优化了。
参考传送门
https://github.com/golang/go/issues/10948
https://github.com/golang/go/blob/0e85fd7561de869add933801c531bf25dee9561c/src/net/tcpsock_posix.go#L47
https://man7.org/linux/man-pages/man2/splice.2.html
建议继续学习:
- 推荐一些socket工具,TCP、UDP调试、抓包工具 (阅读:9426)
- 推荐一些socket工具,TCP、UDP调试、抓包工具 (阅读:7263)
- 用unix socket加速php-fpm、mysql、redis的连接 (阅读:6501)
- 浅析linux kernel network之socket创建 (阅读:5706)
- nginx、php-fpm默认配置与性能–TCP socket还是unix domain socket (阅读:5003)
- web socket 心跳包的实现方案 (阅读:4924)
- python中的socket代理 (阅读:4811)
- netstat和web主机socket文件分析 (阅读:4434)
- 使用socket.io和node.js搭建websocket应用 (阅读:4365)
- php socket为什么这么慢,直到超时 (阅读:3865)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:kxn 来源: Kxn's eXercise Notes
- 标签: golang pipe socket
- 发布时间:2021-05-26 22:49:42
- [55] Go Reflect 性能
- [55] IOS安全–浅谈关于IOS加固的几种方法
- [54] Oracle MTS模式下 进程地址与会话信
- [54] 如何拿下简短的域名
- [53] android 开发入门
- [50] 图书馆的世界纪录
- [49] 读书笔记-壹百度:百度十年千倍的29条法则
- [48] 【社会化设计】自我(self)部分――欢迎区
- [39] 程序员技术练级攻略
- [31] 视觉调整-设计师 vs. 逻辑