golang 解决 socket: too many open files, 以及 too many open files
2024-09-08 18:44:41
同事写的一段代,码业务场景:需要多次GET请求一个三方服务的http 接口,获取数据后写入文件。发现有部分文件没有写入。查看日志出现了报错“socket: too many open files”、“too many open files”。
在此记录一下解决办法。这也是新写Go的人很常见的问题。
示例代码:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
)
func main() {
requestAndWriteFile()
}
// 从接口获取数据并且写入文件
func requestAndWriteFile() {
// 约有5千个元素
params := []string{
"001",
"002",
"003",
"004",
}
hostname := "https://test.com/api"
for _, val := range params {
url := hostname + "?=code" + val
byteData, err := Get(url)
if err != nil{
fmt.Println(err)
continue
}
fileFullPath := "/var/" + val + ".txt"
path := filepath.Dir(fileFullPath)
_, err = os.Stat(path)
// 不存在则创建
if err != nil {
err = os.MkdirAll(path, 0755)
if err != nil {
fmt.Println("创建目录错误", err)
continue
}
}
file, e := os.OpenFile(fileFullPath, os.O_RDWR|os.O_CREATE, 0766)
// 关闭文件
defer file.Close()
if e != nil {
fmt.Println("打开目录错误", e)
}
_, er := file.Write(byteData)
if er != nil {
fmt.Println("写入文件错误", er)
}
}
}
// 发送GET请求
func Get(url string) ([]byte, error) {
response, err := http.Get(url)
if err != nil {
return nil, err
}
// 关闭响应
defer response.Body.Close()
return ioutil.ReadAll(response.Body)
}
上面的代码中使用了“defer response.Body.Close()” 关闭了http响应体,打开的文件也用“defer file.Close()”关闭了。乍一看似乎没有问题。但是Go的HTTP请求本身是有坑的,释放不及时,会造成同时有多个socket连接。第二个问题就是“defer file.Close()” 写在for 循环中,那么按照defer的特性,将在函数requestAndWriteFile return之前执行多个defer,越先出现的defer越后执行。多次循环后打开的文件数就超过了系统限制,就会报错“too many open files”。
解决办法是:对于http请求导致“socket: too many open files”,采用公用的 http.Transport;对于“too many open files”,写入文件的操作,封装成函数,在函数中打开关闭文件,就可以避免。修改后的示例代码:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
)
// 全局 transport
var globalTransport *http.Transport
func init() {
globalTransport = &http.Transport{}
}
func main() {
requestAndWriteFile()
}
func requestAndWriteFile() {
// 约有5千个元素
params := []string{
"001",
"002",
"003",
"004",
}
hostname := "https://test.com/api"
for _, val := range params {
url := hostname + "?=code" + val
byteData, err := Get(url)
if err != nil{
fmt.Println(err)
continue
}
fileFullPath := "/var/" + val + ".txt"
writeFile(fileFullPath, byteData)
}
}
func writeFile(fileFullPath string, byteData []byte) error {
path := filepath.Dir(fileFullPath)
_, err := os.Stat(path)
// 不存在则创建
if err != nil {
err = os.MkdirAll(path, 0755)
if err != nil {
fmt.Println("创建目录错误")
return err
}
}
file, e := os.OpenFile(fileFullPath, os.O_RDWR|os.O_CREATE, 0766)
// 一定要close
defer file.Close()
if e != nil {
fmt.Println("打开目录错误")
}
_, er := file.Write(byteData)
if er != nil {
fmt.Println("写入文件错误")
return er
}
return nil
}
// 发送get请求
func Get(uri string) ([]byte, error) {
client := http.Client{
Transport: globalTransport,
}
res, err := client.Get(uri)
if err != nil {
return nil, err
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
return body, nil
}
如果确实有必要同时打开超过系统限制的多个文件,那么可以使用ulimit 命令修改。
最新文章
- clock()、time()、clock_gettime()和gettimeofday()函数的用法和区别【转】
- Lua 5.2 编译 For Windows
- Linux操作系统shell与函数详解
- 六间房PK同时观看两方视频(绕过VIP限制)+直播状态批量监测
- RESTful 架构理解
- OpenFileDialog组件打开文件....待续
- fopen()功能
- git for windows (又名 msysgit)如何记住用户名和密码
- 关于struts2 Could not find action or result错误
- halcon c++ 异常处理
- 微信公众号开发C#系列-7、消息管理-接收事件推送
- linux 基础知识(三)
- ERROR: Field * doesn't have a default value
- 一步一步学Python(3) 基础补充
- D Tree Requests dfs+二分 D Pig and Palindromes -dp
- 使用httpClient模拟http请求
- 零宽断言 -- Lookahead/Lookahead Positive/Negative
- linux修改文件系统挂载的目录
- 洛谷 P4783 【模板】矩阵求逆
- libxml2 在mingw中 xmlfree连接错误问题
热门文章
- 错误 : 资产文件&;ldquo;\obj\project.assets.json&;rdquo;没有&;ldquo;.NETCoreApp,Version=v2.0&;rdquo;的目标。确保已运行还原,且&;ldquo;netcoreapp2.0&;rdquo;已包含在项目的 TargetFrameworks 中。
- 前端使用xlsx file-saver xlsx-style导出
- Appium 入门
- dcat-admin主题
- Dockerfile打包java应用
- Spring中最常用的11个扩展点
- Flink Table API &; SQL 自定义Redis Sink 使用方式
- 第五章:用Python分析商品退单数据并找出异常商品
- linux run/media/wang/centos_磁盘爆满
- props其他-混入mixin-插件-elementui使用-localStorage系列-vueRouter-vuex