Golang HTTP客户端 - Connectex:通常允许每个套接字地址(协议/网络地址/端口)的一个用法

发布于 2025-02-12 10:19:53 字数 2594 浏览 1 评论 0原文

我正在尝试创建基于Golang Net/HTTP的Web服务,该服务从用户那里获取JSON输入,然后查询第三方Web API ITSELVES,以获取数据,然后返回对客户端的答案。我使用http.client {} client.do()方法来查询第三方Web API。当我执行性能测试(JMeter)时,通常会出现错误: Connectex:通常允许每个套接字地址(协议/网络地址/端口)使用一个用法。我怀疑种族状态,但种族检测没有任何种族状态。在我看来,发布http请求的频率很高,可能会随着时间的流逝而导致端口耗尽。因此,我试图在两个或三个实例(主机)之间平衡查询。但是,我仍然经常出现错误。有没有办法避免此错误?有没有建筑技巧,可以使我的服务高负载准备好?您可以在下面看到简化的代码示例:

var upstream = []string{"1.1.1.1", "2.2.2.2", "3.3.3.3"}
var scheme = "http"
var timeout = 5

    func main() {
        fmt.Println("Listening on *: 8080")
        http.HandleFunc("/", handler)
        log.Fatal(http.ListenAndServe(":8080", nil))
    }
    
    func handler(w http.ResponseWriter, r *http.Request) {
    
        if r.Method != "POST" {
            http.Error(w, http.StatusText(405), 405)
            return
        }
    
        body, err := ioutil.ReadAll(r.Body)
        if err != nil {
            fmt.Println("Error:", err)
            http.Error(w, http.StatusText(400), 400)
            return
        }
    
        var jsonEntity = make(map[string]string)
    
        json.Unmarshal(body, &jsonEntity)
    
        payload := bytes.NewReader(body)
    
        url := r.URL
        url.Host = upstream[0]
        if len(upstream) > 1 {
            url.Host = upstream[random(0, len(upstream))]
        }
        url.Scheme = scheme
    
    
        proxyReq, err := http.NewRequest(http.MethodPost, url.String(), payload)
        if err != nil {
            fmt.Println("Error:", err)
            http.Error(w, http.StatusText(400), 400)
            return
        }
    
        client := http.Client{Timeout: time.Duration(timeout) * time.Second}
    
        response, err := client.Do(proxyReq) // <- IT FAILS HERE!!!
        if err != nil {                 
            fmt.Println("Error:", err)
            http.Error(w, http.StatusText(500), 500)
            sendWithCheck(w, "%s", []byte(`{"answerParam":"error"}`))
            return
        }
        defer func() {
            if err := response.Body.Close(); err != nil {
                log.Println("Error:", err)
            }
        }()
    
        res, err := ioutil.ReadAll(response.Body)
        if err != nil {
            fmt.Println("Error:", err)
            http.Error(w, http.StatusText(500), 500)
            sendWithCheck(w, "%s", []byte(`{"answerParam":"error"}`))
            return
        }
    
        if response.StatusCode == 200 {
            sendWithCheck(w, "%s", res)
            return
        }
    
        w.WriteHeader(response.StatusCode)
        return
    }

I`m trying to create a Golang net/http based web service, which takes a JSON input from user, and then it queries a third party web API itselves, to get data and then returns answer to client. I use http.Client{} client.Do() method to query the third party web API. When I perform a performance test (JMeter), very often I get the error: connectex: Only one usage of each socket address (protocol/network address/port) is normally permitted. I suspected the race state, but race detection shows no state of race. To my mind, posting HTTP requests too often, may cause port exhaustion over time. So, I tried to balance queries between two or three instances (hosts). However, I still get the error rather often. Is there a way to avoid this error? Is there any architecture trick, to make my service high-load ready? You can see the simplified code example below:

var upstream = []string{"1.1.1.1", "2.2.2.2", "3.3.3.3"}
var scheme = "http"
var timeout = 5

    func main() {
        fmt.Println("Listening on *: 8080")
        http.HandleFunc("/", handler)
        log.Fatal(http.ListenAndServe(":8080", nil))
    }
    
    func handler(w http.ResponseWriter, r *http.Request) {
    
        if r.Method != "POST" {
            http.Error(w, http.StatusText(405), 405)
            return
        }
    
        body, err := ioutil.ReadAll(r.Body)
        if err != nil {
            fmt.Println("Error:", err)
            http.Error(w, http.StatusText(400), 400)
            return
        }
    
        var jsonEntity = make(map[string]string)
    
        json.Unmarshal(body, &jsonEntity)
    
        payload := bytes.NewReader(body)
    
        url := r.URL
        url.Host = upstream[0]
        if len(upstream) > 1 {
            url.Host = upstream[random(0, len(upstream))]
        }
        url.Scheme = scheme
    
    
        proxyReq, err := http.NewRequest(http.MethodPost, url.String(), payload)
        if err != nil {
            fmt.Println("Error:", err)
            http.Error(w, http.StatusText(400), 400)
            return
        }
    
        client := http.Client{Timeout: time.Duration(timeout) * time.Second}
    
        response, err := client.Do(proxyReq) // <- IT FAILS HERE!!!
        if err != nil {                 
            fmt.Println("Error:", err)
            http.Error(w, http.StatusText(500), 500)
            sendWithCheck(w, "%s", []byte(`{"answerParam":"error"}`))
            return
        }
        defer func() {
            if err := response.Body.Close(); err != nil {
                log.Println("Error:", err)
            }
        }()
    
        res, err := ioutil.ReadAll(response.Body)
        if err != nil {
            fmt.Println("Error:", err)
            http.Error(w, http.StatusText(500), 500)
            sendWithCheck(w, "%s", []byte(`{"answerParam":"error"}`))
            return
        }
    
        if response.StatusCode == 200 {
            sendWithCheck(w, "%s", res)
            return
        }
    
        w.WriteHeader(response.StatusCode)
        return
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文