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

时间:2015-08-04 04:26:10

标签: sockets go

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

panic: Get http://localhost:8088/: dial tcp 127.0.0.1:8088: can't assign requested address

奇怪的是,如果我增加定时器延迟(即从毫秒到微秒),则需要更多的命中才能获得错误,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()
}

1 个答案:

答案 0 :(得分:5)

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

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

在客户端上设置一个变容器以防止过多的同时连接甚至可能是有利的,这会导致连接再次过快地再循环。