同事写的一段代,码业务场景:需要多次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 命令修改。

最新文章

  1. clock()、time()、clock_gettime()和gettimeofday()函数的用法和区别【转】
  2. Lua 5.2 编译 For Windows
  3. Linux操作系统shell与函数详解
  4. 六间房PK同时观看两方视频(绕过VIP限制)+直播状态批量监测
  5. RESTful 架构理解
  6. OpenFileDialog组件打开文件....待续
  7. fopen()功能
  8. git for windows (又名 msysgit)如何记住用户名和密码
  9. 关于struts2 Could not find action or result错误
  10. halcon c++ 异常处理
  11. 微信公众号开发C#系列-7、消息管理-接收事件推送
  12. linux 基础知识(三)
  13. ERROR: Field * doesn't have a default value
  14. 一步一步学Python(3) 基础补充
  15. D Tree Requests dfs+二分 D Pig and Palindromes -dp
  16. 使用httpClient模拟http请求
  17. 零宽断言 -- Lookahead/Lookahead Positive/Negative
  18. linux修改文件系统挂载的目录
  19. 洛谷 P4783 【模板】矩阵求逆
  20. libxml2 在mingw中 xmlfree连接错误问题

热门文章

  1. 错误 : 资产文件“\obj\project.assets.json”没有“.NETCoreApp,Version=v2.0”的目标。确保已运行还原,且“netcoreapp2.0”已包含在项目的 TargetFrameworks 中。
  2. 前端使用xlsx file-saver xlsx-style导出
  3. Appium 入门
  4. dcat-admin主题
  5. Dockerfile打包java应用
  6. Spring中最常用的11个扩展点
  7. Flink Table API & SQL 自定义Redis Sink 使用方式
  8. 第五章:用Python分析商品退单数据并找出异常商品
  9. linux run/media/wang/centos_磁盘爆满
  10. props其他-混入mixin-插件-elementui使用-localStorage系列-vueRouter-vuex