如何关闭Golang中的HTTP连接 How to Close Golang's HTTP connection
我们的一个服务是用Go写的,在测试的时候发现几个小时之后它就会core掉,而且core的时候没有打出任何堆栈信息,简单分析后发现该服务中的几个HTTP服务的连接数不断增长,而我们的开发机的fd limit只有1024,当该服务所属进程的连接数增长到系统的fd limit的时候,它被操作系统杀掉了。。。
HTTP Connection中连接未被释放的问题在https://groups.google.com/forum/#!topic/golang-nuts/wliZf2_LUag和https://groups.google.com/forum/#!topic/golang-nuts/tACF6RxZ4GQ都有提到。
这个服务中,我们会定期向一个HTTP服务器发起POST请求,因为请求非常不频繁,所以想采用短连接的方式去做。请求代码大概长这样:
func dialTimeout(network, addr string) (net.Conn, error) {
return net.DialTimeout(network, addr, time.Second*POST_REMOTE_TIMEOUT)
}
func DoRequest(URL string) xx, error {
transport := http.Transport{
Dial: dialTimeout,
}
client := http.Client{
Transport: &transport,
}
content := RequestContent{}
// fill content here
postStr, err := json.Marshal(content)
if err != nil {
return nil, err
}
resp, err := client.Post(URL, "application/json", bytes.NewBuffer(postStr))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// receive body, handle it
}
运行这段代码一段时间后会发现,该进程下面有一堆ESTABLISHED状态的连接(用lsof -p pid查看某进程下的所有fd),因为每次DoRequest函数被调用后,都会新建一个TCP连接,如果对端不先关闭该连接(对端发FIN包)的话,我们这边即便是调用了resp.Body.Close()函数仍然不会改变这些处于ESTABLISHED状态的连接。为什么会这样呢?只有去源代码一探究竟了。
Golang的net包中client.go, transport.go, response.go和request.go这几个文件中实现了HTTP Client。当应用层调用client.Do()函数后,transport层会首先找与该请求相关的已经缓存的连接(这个缓存是一个map,map的key是请求方法、请求地址和proxy地址,value是一个叫persistConn的连接描述结构),如果已经有可以复用的旧连接,就会在这个旧连接上发送和接受该HTTP请求,否则会新建一个TCP连接,然后在这个连接上读写数据。当client接受到整个响应后,如果应用层没有
调用response.Body.Close()函数,刚刚传输数据的persistConn就不会被加入到连接缓存中,这样如果您在下次发起HTTP请求的时候,就会重新建立TCP连接,重新分配persistConn结构,这是不调用response.Body.Close()的一个副作用。
如果不调用response.Body.Close()还存在一个问题。如果请求完成后,对端关闭了连接(对端的HTTP服务器向我发送了FIN),如果这边不调用response.Body.Close(),那么可以看到与这个请求相关的TCP连接的状态一直处于CLOSE_WAIT状态(还记得么?CLOSE_WAIT是连接的半开半闭状态,它是收到对方的FIN并且我们也发送了ACK,但是本端还没有发送FIN到对端,如果本段不调用close关闭连接,那么连接将一直处于
CLOSE_WAIT状态,不会被系统回收)。
调用了response.Body.Close()就万无一失了么?上面代码中也调用了body.Close()为什么还会有很多ESTABLISHED状态的连接呢?因为在函数DoRequest()的每次调用中,我们都会新创建transport和client结构,当HTTP请求完成并且接收到响应后,如果对端的HTTP服务器没有关闭连接,那么这个连接会一直处于ESTABLISHED状态。如何解呢?
有两个方法:
第一个方法是用一个全局的client,函数DoRequest()中每次都只在这个全局client上发送数据。但是如果我就想用短连接呢?用方法二。
第二个方法是在transport分配时将它的DisableKeepAlives参数置为false,像下面这样:
// ...
transport := http.Transport{
Dial: dialTimeout,
DisableKeepAlives: true,
} client := http.Client{
Transport: &transport,
}
// ...
从transport.go:L908可以看到,当应用层调用resp.Body.Close()时,如果DisableKeepAlives被开启,那么transport自动关闭本端连接。而不将它加入到连接缓存中。
补充一下,在dialTimeout函数中disable tcp连接的keepalive选项是不可行的,它只是设置TCP连接的选项,不会影响到transport中对连接的控制。
func dialTimeout(network, addr string) (net.Conn, error) {
conn, err := net.DialTimeout(network, addr, time.Second*POST_REMOTE_TIMEOUT)
if err != nil {
return conn, err
}
tcp_conn := conn.(*net.TCPConn)
tcp_conn.SetKeepAlive(false)
return tcp_conn, err
}
如何关闭Golang中的HTTP连接 How to Close Golang's HTTP connection的更多相关文章
- golang中mysql建立连接超时时间timeout 测试
本文测试连接mysql的超时时间. 这里的"连接"是建立连接的意思. 连接mysql的超时时间是通过参数timeout设置的. 1.建立连接超时测试 下面例子中,设置连接超时时间为 ...
- golang中使用gorm连接mysql操作
一.代码 package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/go- ...
- Android真机调试——远程主机强迫关闭了一个现有的连接。
以前用真机调试程序的时候,Android Studio 出现如下的错误 [2016-11-12 10:37:36 - DeviceMonitor] Adb connection Error:远程主机强 ...
- golang中jwt使用
golang 中jwt使用方式总结. 1. golang示例代码 import ( "fmt" "time" "github.com/dgrijalv ...
- golang中最大协程数的限制(线程)
golang中最大协程数的限制 golang中有最大协程数的限制吗?如果有的话,是通过什么参数控制呢?还是通过每个协程占用的资源计算? 通过channel控制协程数的就忽略吧. 以我的理解,计算机资源 ...
- 基础知识 - Golang 中的正则表达式
------------------------------------------------------------ Golang中的正则表达式 ------------------------- ...
- Golang中的自动伸缩和自防御设计
Raygun服务由许多活动组件构成,每个组件用于特定的任务.其中一个模块是用Golang编写的,负责对iOS崩溃报告进行处理.简而言之,它接受本机iOS崩溃报告,查找相关的dSYM文件,并生成开发者可 ...
- Golang中的RegExp正则表达式用法指南
------------------------------------------------------------ Golang中的正则表达式 ------------------------- ...
- python golang中grpc 使用示例代码详解
python 1.使用前准备,安装这三个库 pip install grpcio pip install protobuf pip install grpcio_tools 2.建立一个proto文件 ...
随机推荐
- 全文检索(SOLR)前端应用浅析 (转)
最近在一个关于知识管理系统中检索的一个功能方案,找到了一个很好的参考应用方案嘎要的分析一下,希望有类似应用的可以交流交流. 提起全文检索,Lucene的大名估计地球人都知道,通过这么多年的发展,外围的 ...
- layer-list shape drawable 层叠背景 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- Android -- MediaRecord
MediaRecord 集成了录音.编码.压缩等,支持少量的录音音频格式,大概有.aac .amr .3gp 优点:大部分以及集成,直接调用相关接口即可,代码量小 缺点:无法实时处理音频:输出的音频格 ...
- capwap学习笔记——初识capwap(五)
3. CAPWAP Binding for IEEE 802.11 ¢ CAPWAP协议本身并不包括任何指定的无线技术.它依靠绑定协议来扩展对特定无线技术的支持. ¢ RFC5416就是用来扩展CAP ...
- Pandas DataFrame笔记
1.属性方式,可以用于列,不能用于行 2.可以用整数切片选择行,但不能用单个整数索引(当索引不是整数时) 3.直接索引可以使用列.列集合,但不能用索引名索引行 用iloc取行,得到的series: d ...
- Material Design学习之 ProgreesBar
转载奇怪注明出处:王亟亟的大牛之路 继续我们Material Design的内容,这一篇讲的是进度条,上一篇是Switch地址例如以下:http://blog.csdn.net/ddwhan0123/ ...
- c/c++ 函数指针的用法
[目录] 基本定义 c 函数指针使用举例 c++ 函数指针使用举例 函数指针作为函数参数 函数指针作为函数返回值 函数指针数组 typedef 简化函数指针操作 c语言函数指针的定义形式:返回类型 ( ...
- linux下jdk_tomcat+mysql配置那点事
(1)上传环境压缩包 apache-tomcat-7.0.47.tar.gz, jdk-7u71-linux-x64.gz, mysql-5.6.14.tar.gz通过xftp工具上传到/usr/lo ...
- JAVA设计模式——第 2 章 代理模式【Proxy Pattern】(转)
什么是代理模式呢?我很忙,忙的没空理你,那你要找我呢就先找我的代理人吧,那代理人总要知道被代理人能做哪些事情不能做哪些事情吧,那就是两个人具备同一个接口,代理人虽然不能干活,但是被代理的人能干活呀. ...
- Android Studio导入Eclipse项目的两种方法
Android Studio导入Eclipse项目有两种方法,一种是直接把Eclipse项目导入Android Studio,另一种是在Eclipse项目里面进行转换,然后再导入Android Stu ...