记一次HTTP请求返回EOF的问题

11,217 阅读2分钟

记一次HTTP请求返回EOF的问题

背景:

对接第三方接口,需要频繁的发送http请求,刚开始使用sync.Pool池做http请求池,后来为了统一项目管理,
修改为了项目中自己的http请求方法,修改之后再次请求,出现异常,
表现是请求异常缓慢,且极大概率response读取到的是EOF

排查:

1、因为http客户端设置了超时,所以放大了超时时间到5秒,但是并没没有缓解情况,再次扩大时间到10秒,还是不行,
最后直接不设置超时时间,也不行。所以不是设置的超时的锅
2、检查网络是否通畅。没有问题,不是网络的锅
3、切换回池,正常
4、检查http客户端设置是否正常,发现最里面的生成客户端的函数中设置了一个DisableKeepAlives的参数为false,
看注释意思是:为true的时候不保持连接,false保持连接,尝试修改为true,问题得到解决
5、但是项目中的所有客户端都需要保持连接,所以这个参数不能被修改,在次查找得知了req中有一个Close参数,等价与
DisableKeepAlives,所以自己重新封装了一个函数,再次尝试成功

结论:

重复使用了一个TCP连接,导致的问题,但是对方给的文档也没有提示,暂且猜测对方的api不支持连接保持

思考:

为什么使用池就没问题呢?
根据官方文档的说明,池中的对象不会保持状态,所以当每次从池中获取连接时,都是一个全新的连接,所以不会出现问题

而自己new的http客户端,除非指定,否则go会尝试使用已经存在的TCP连接,导致了这个问题

代码:

// Request 不重用同一个连接
func RequestWithOnly(addr, method string, heads map[string]string, data []byte, timeOut time.Duration, username, password string) ([]byte, error) {
	client, err := newHTTPSClient(addr, timeOut)
	if err != nil {
		return nil, err
	}
	
	reader := bytes.NewReader(data)
	req, err := http.NewRequest(method, addr, reader)
	if err != nil {
		return nil, err
	}
	
	if username != "" && password != "" {
		req.SetBasicAuth(username, password)
	}

	req.Header.Set("Content-Type", "application/json")
	for k, v := range heads {
		req.Header.Set(k, v)
	}
	req.Close = true
	
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	
	r, err := ioutil.ReadAll(resp.Body)
	return r, err
 }