【问题标题】:go lang http server 503 error with loadgo lang http server 503 负载错误
【发布时间】:2017-10-14 22:20:29
【问题描述】:

我为目的测试编写了以下 GO 程序。这个 http 服务器接收 get 请求并对另一个 rest 服务进行 http 调用。该程序运行良好,但是当我在 2vCPUs 8 GB 盒子中运行负载测试时。它在大约 500 TPS 后开始提供 Http 503。

func retrievedata(w http.ResponseWriter, r *http.Request){

    client := &http.Client{
        Timeout: time.Second * 5,
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{
                InsecureSkipVerify: true,
            },
        },
    }

    w.Header().Set("Content-Type", "application/json")
    urlstring, _ := url.Parse("https://service.dot.com/backendservice/")

    req, _ := http.NewRequest("GET", endpointurl, nil)
    req.Header.Set("Accept", "application/json")
    req.Header.Set("Content-Type", "application/json")
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println(err)
    }

    defer resp.Body.Close()


    switch resp.StatusCode {
    case 200:

        data, _ := ioutil.ReadAll(resp.Body)
        w.Write(data)
    case 404:
        w.WriteHeader(http.StatusNotFound)
        data, _ := ioutil.ReadAll(resp.Body)
        w.Write(data)
    case 500:
        w.WriteHeader(http.StatusInternalServerError)
        data, _ := ioutil.ReadAll(resp.Body)
        w.Write(data)
    default:
        w.WriteHeader(http.StatusNoContent)
    }

}


func main() {
    fmt.Println("this is a main function")
    http.HandleFunc("/getdata", retrievedata)
    err := http.ListenAndServe(":8191", nil)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println("Service is Running at port 8191")
}

然后我添加了 go 例程来生成处理函数

go http.HandleFunc("/getdata", retrievedata)

这次我看到 TPS 略有增加,但在大约 600 TPS 后仍然出现 503 错误。请注意,其他的休息功能已经过 2000TPS 测试,所以我确信这没有问题。 我应该做些不同的事情来获得更高的 TPS 吗?

【问题讨论】:

  • 我建议取消隐藏所有错误并写入它们。你能检查资源:打开的文件、套接字等等吗?接下来尝试使用golang.org/pkg/net/http/pprof 查找CPU 和内存配置文件。
  • go http.HandleFunc 不是一个好主意。 http 服务器已经在一个单独的 goroutine 中处理每个请求,并且调用 go http.HandleFunc 甚至不会这样做——所做的只是在启动阶段启动一个 goroutine,它将立即退出。简而言之:那个 goroutine 没有添加任何东西(除了可能在启动期间的竞争条件)

标签: go


【解决方案1】:

如果您查看transport.go,您会看到:

var DefaultTransport RoundTripper = &Transport{
    //... 
    MaxIdleConns:          100,
    IdleConnTimeout:       90 * time.Second,
    //... 
}

// DefaultMaxIdleConnsPerHost is the default value of Transport's
// MaxIdleConnsPerHost.
const DefaultMaxIdleConnsPerHost = 2

MaxIdleConns: 100 将连接池的大小设置为 100 个连接,但DefaultMaxIdleConnsPerHost 将每个主机设置为 2 个。

所以本质上,您的连接池只能容纳 2 个套接字。因此,如果您要执行 100 个并发请求,一旦它们完成,池中的 2 个套接字将保持打开状态,而其他 98 个套接字将关闭并最终处于TIME_WAIT 状态。

由于这是在负载测试工具的 goroutine 中发生的,因此您只会在 TIME_WAIT 状态下累积数千个连接。最终,您将用完临时端口并且无法打开新的客户端连接。

defaultRoundTripper := http.DefaultTransport
defaultTransportPtr, ok := defaultRoundTripper.(*http.Transport)
if !ok {
    panic(fmt.Sprintf("defaultRoundTripper not an *http.Transport"))
}
defaultTransport := *defaultTransportPtr 
defaultTransport.MaxIdleConns = 1000
defaultTransport.MaxIdleConnsPerHost = 1000

client = &http.Client{Transport: &defaultTransport}

最重要的是,你正在做的很多工作,你不需要对每个请求都做。你可以这样做:

var client *http.Client
var endpointurl string
var req http.Request

func init() {   
    defaultRoundTripper := http.DefaultTransport
    defaultTransportPtr, ok := defaultRoundTripper.(*http.Transport)
    if !ok {
        panic(fmt.Sprintf("defaultRoundTripper not an *http.Transport"))
    }
    defaultTransport := *defaultTransportPtr 
    defaultTransport.MaxIdleConns = 1000
    defaultTransport.MaxIdleConnsPerHost = 1000
    defaultTransport.TLSClientConfig = &tls.Config{
        InsecureSkipVerify: true,
    }
    client = &http.Client{Transport: }
    client = &http.Client{
        Timeout: time.Second * 5,
        Transport: &defaultTransport
    }
    endpointurl, _ = url.Parse("https://service.dot.com/backendservice/")
    req, _ := http.NewRequest("GET", endpointurl, nil)
    req.Header.Set("Accept", "application/json")
    req.Header.Set("Content-Type", "application/json")
}

func retrievedata(w http.ResponseWriter, r *http.Request){
    w.Header().Set("Content-Type", "application/json")
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println(err)
    }
    defer resp.Body.Close()
    switch resp.StatusCode {
    case 200:
        data, _ := ioutil.ReadAll(resp.Body)
        w.Write(data)
    case 404:
        w.WriteHeader(http.StatusNotFound)
        data, _ := ioutil.ReadAll(resp.Body)
        w.Write(data)
    case 500:
        w.WriteHeader(http.StatusInternalServerError)
        data, _ := ioutil.ReadAll(resp.Body)
        w.Write(data)
    default:
        w.WriteHeader(http.StatusNoContent)
    }

}


func main() {
    fmt.Println("this is a main function")
    http.HandleFunc("/getdata", retrievedata)
    err := http.ListenAndServe(":8191", nil)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println("Service is Running at port 8191")
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-23
    • 1970-01-01
    • 2012-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-28
    相关资源
    最近更新 更多