在Go语言中,tryPutIdleConn方法是在http.Transport中用于将空闲连接放回连接池的方法。当一个HTTP请求完成后,如果连接池中没有达到最大限制,那么该连接就会被放回连接池中,以备下一次使用。 tryPutIdleConn方法的实现逻辑如下: 1. 首先,它会检查连接是否已经关闭,如果已经关闭,则直接返回。 2. 然后,它会检查连接池是否已经满了,如果已经满了,则直接关闭连接。 3. 接着,它会检查连接是否已经在连接池中,如果已经在连接池中,则直接返回。 4. 最后,如果连接没有被关闭,连接池没有满,连接也不在连接池中,那么就将连接放回连接池中。 总的来说,tryPutIdleConn方法的作用是将空闲连接放回连接池中,以便下一次使用。
设置最大空闲连接数和最大空闲时间
在Golang中,可以通过设置http.Transport的MaxIdleConns和IdleConnTimeout字段来控制空闲连接数和空闲时间。 MaxIdleConns表示最大空闲连接数,即在连接池中保持的最大空闲连接数。如果超过这个数目,将会关闭一些空闲连接。可以根据实际情况设置这个值,一般建议设置为CPU核心数的2倍。 IdleConnTimeout表示空闲连接的最大空闲时间,即连接在连接池中保持的最长时间。如果超过这个时间,连接将会被关闭。可以根据实际情况设置这个值,一般建议设置为几分钟。 例如,以下代码设置了最大空闲连接数为100,最大空闲时间为5分钟:
transport := &http.Transport{ MaxIdleConns: 100, IdleConnTimeout: 5 * time.Minute, } client := &http.Client{Transport: transport}通过合理设置这两个参数,可以提高HTTP请求的效率和性能。
http请求示例
// 创建一个自定义的Transport结构体
tr := &http.Transport{
// 最大空闲连接数
MaxIdleConns: 10,
// 空闲连接超时时间
IdleConnTimeout: 30 * time.Second,
// 禁用压缩
DisableCompression: true,
}
// 创建一个http.Client结构体,并设置其Transport字段为自定义的Transport结构体
client := &http.Client{Transport: tr}
// 发送HTTP请求
resp, err := client.Get("https://www.baidu.com")
if err != nil {
fmt.Println(err)
return
}
defer resp.Body.Close()
// 处理响应
fmt.Println(resp.StatusCode)
系统默认的空闲时间和最大空闲连接数
var DefaultTransport RoundTripper = &Transport{
Proxy: ProxyFromEnvironment,
DialContext: defaultTransportDialContext(&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}),
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
http包
func (c *Client) Get(url string) (resp *Response, err error)
func (c *Client) do(req *Request) (retres *Response, reterr error)
func (c *Client) send(req *Request, deadline time.Time) (resp *Response, didTimeout func() bool, err error)
transport包
func (t *Transport) roundTrip(req *Request) (*Response, error)
func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (pc *persistConn, err error)
func (t *Transport) queueForDial(w *wantConn)
func (t *Transport) dialConnFor(w *wantConn)
func (t *Transport) putOrCloseIdleConn(pconn *persistConn)
func (t *Transport) tryPutIdleConn(pconn *persistConn) error
详情函数tryPutIdleConn
func (t *Transport) tryPutIdleConn(pconn *persistConn) error {
if t.DisableKeepAlives || t.MaxIdleConnsPerHost < 0 {
return errKeepAlivesDisabled
}
if pconn.isBroken() {
return errConnBroken
}
pconn.markReused()
t.idleMu.Lock()
defer t.idleMu.Unlock()
// HTTP/2 (pconn.alt != nil) connections do not come out of the idle list,
// because multiple goroutines can use them simultaneously.
// If this is an HTTP/2 connection being “returned,” we're done.
if pconn.alt != nil && t.idleLRU.m[pconn] != nil {
return nil
}
// Deliver pconn to goroutine waiting for idle connection, if any.
// (They may be actively dialing, but this conn is ready first.
// Chrome calls this socket late binding.
// See https://www.chromium.org/developers/design-documents/network-stack#TOC-Connection-Management.)
key := pconn.cacheKey
// 判断等待空闲连接的队列是否为空
if q, ok := t.idleConnWait[key]; ok {
done := false
if pconn.alt == nil {
// HTTP/1.
// Loop over the waiting list until we find a w that isn't done already, and hand it pconn.
for q.len() > 0 {
// 从头部取出来一个
w := q.popFront()
if w.tryDeliver(pconn, nil) {
done = true
break
}
}
} else {
// HTTP/2.
// Can hand the same pconn to everyone in the waiting list,
// and we still won't be done: we want to put it in the idle
// list unconditionally, for any future clients too.
for q.len() > 0 {
w := q.popFront()
w.tryDeliver(pconn, nil)
}
}
// 如果队列空了
if q.len() == 0 {
// 删除队列的map
delete(t.idleConnWait, key)
} else {
// 将空闲连接的map指向了新的队列
t.idleConnWait[key] = q
}
if done {
return nil
}
}
if t.closeIdle {
return errCloseIdle
}
if t.idleConn == nil {
t.idleConn = make(map[connectMethodKey][]*persistConn)
}
idles := t.idleConn[key]
// 空闲连接已经是最大了,则抛弃此连接
if len(idles) >= t.maxIdleConnsPerHost() {
return errTooManyIdleHost
}
// 判断此连接是否存在,不存在抛出错误
for _, exist := range idles {
if exist == pconn {
log.Fatalf("dup idle pconn %p in freelist", pconn)
}
}
// 连接放入到连接池中
t.idleConn[key] = append(idles, pconn)
t.idleLRU.add(pconn)
if t.MaxIdleConns != 0 && t.idleLRU.len() > t.MaxIdleConns {
oldest := t.idleLRU.removeOldest()
oldest.close(errTooManyIdle)
t.removeIdleConnLocked(oldest)
}
// Set idle timer, but only for HTTP/1 (pconn.alt == nil).
// The HTTP/2 implementation manages the idle timer itself
// (see idleConnTimeout in h2_bundle.go).
if t.IdleConnTimeout > 0 && pconn.alt == nil {
if pconn.idleTimer != nil {
pconn.idleTimer.Reset(t.IdleConnTimeout)
} else {
// 空闲连接时间到了之后,将会被关闭
pconn.idleTimer = time.AfterFunc(t.IdleConnTimeout, pconn.closeConnIfStillIdle)
}
}
pconn.idleAt = time.Now()
return nil
}
连接队列的key
type connectMethodKey struct {
proxy, scheme, addr string
onlyH1 bool
}