多个http.Requests给出"无法分配请求的地址",除非加速

All*_*uce 0 sockets go

使用下面的客户端代码(以及此框中端口8088上的侦听Web服务器),在此错误弹出之前,我很少能够获得超过23000次点击client.Get():

panic: Get http://localhost:8088/: dial tcp 127.0.0.1:8088: can't assign requested address
Run Code Online (Sandbox Code Playgroud)

奇怪的是,如果我增加定时器延迟(即从毫秒到微秒),则需要更多的命中来获得错误,170,000甚至更多.

查看网络流量,每个客户端连接在断开连接之前仅使用少量次数(即客户端发送FIN).很明显,它正在建立许多TCP连接并溢出套接字表.鉴于Golang HTTP文档默认启用了keepalive,我不希望这样.内核跟踪显示在关闭之前底层套接字没有发出错误(EAGAIN除外,它是预期的并且不总是在套接字关闭之前).

这与OSX上的Go 1.4.2(14.4.0)有关.为什么客户端连接不能一直重用?

package main

import (
    "io/ioutil"
    "net/http"
    "runtime"
    "sync"
    "time"
)

var reqnum = 0

func hit(client *http.Client) {
    resp, err := client.Get("http://localhost:8088/")
    if err != nil {
        println(reqnum)
        panic(err)
    }
    defer resp.Body.Close()
    _, err = ioutil.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }
    reqnum++ // not thread safe, but shouldn't cause errors.
}

func main() {
    var wg sync.WaitGroup
    runtime.GOMAXPROCS(runtime.NumCPU())
    client := &http.Client{}
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            ticker := time.NewTicker(time.Microsecond * 1000)
            for j := 0; j < 120000; j++ {
                <-ticker.C
                hit(client)
            }
            ticker.Stop()
        }()
    }
    wg.Wait()
}
Run Code Online (Sandbox Code Playgroud)

Jim*_*imB 5

can't assign requested address拨号期间的错误是由用尽客户端连接的本地临时端口耗尽引起的.你的端口耗尽的原因很简单,因为你连接太多,速度太快.当您加快连接速率时会发生什么,是您在关闭之前开始捕获返回池中的空闲连接.在拨号期间捕获这些新空闲连接以更快地返回连接的代码路径,但是无法每次都确定地捕获这些连接.

由于您只连接到一个主机(如评论中所述),您需要做的是设置Transport.MaxIdleConnsPerHost更高的值.您需要查看它在何处平衡,在太多打开的连接之间,以及当您开始过快地回收它们时.

在客户端上使用变容二极管以防止过多的同时连接甚至可能是有利的,这会导致连接再次过快地再循环.