间歇性错误getsockopt:Http Post上的连接被拒绝错误

时间:2018-02-19 15:09:54

标签: rest http post go

有两个go应用程序,一个是 stapi 在端口8050上侦听并提供RESTful API,另一个是客户端来使用这些API。

两者都在不同的服务器上运行,客户端在使用HTTP POST方法调用API时经常出错。以下是来自客户端日志的几行(真实IP被假想的替换)

2018/02/17 11:42:58 ERROR: [DoLogin] API Error: [Post https://123.123.123.123:8050/v1/st/verifyuser: dial tcp 123.123.123.123:8050: getsockopt: connection refused]
2018/02/17 11:47:14 ERROR: [CreateAttempt] Error: [Post https://123.123.123.123:8050/v1/userattempts/createattempt: dial tcp 123.123.123.123:8050: getsockopt: connection refused]

它是间歇性的并且使应用程序不可靠,在大约1k请求中我得到了大约50+请求的错误。

最初 stapi 正在侦听所有IP

httpSrv := http.Server{
    Addr:         ":8050",
    Handler:      router, // < gin router
    ...
}

但在阅读了Golang HTTP Post error: connection refused中的workaroung之后,我修改了 stapi 应用程序并让它在不同的IP上进行侦听,如下所示

$ sudo lsof -i -P -n | grep LISTEN
stapi     4775  samtech   10u  IPv4 2388179      0t0  TCP 123.123.123.123:8050 (LISTEN)
stapi     4775  samtech   11u  IPv6 2388181      0t0  TCP [::1]:8050 (LISTEN)
stapi     4775  samtech   12u  IPv4 2388183      0t0  TCP 127.0.0.1:8050 (LISTEN)

但问题仍然存在,我还应该检查和修复什么?请建议。

API受JWT保护,以下是客户端发出POST请求的方式

func (w *OST) DoLogin(c *gin.Context) {
    ...
    ud := stapimodels.UserLogin{}
    err := c.BindJSON(&ud)
    ...
    //call api to save user response
    url := config.AppConfig.APIBaseURL + "st/verifyuser"
    res, err := api.JwtApi.APIPost(url, &ud)
    if err != nil {
            g.Logger.Errorm("DoLogin", "Error: %v", err)
            t.Error("Error", err.Error())
            return
    }
    ...
}

//APIPost - call given apiurl with POST method and pass data
func (j *JwtAPI) APIPost(apiurl string, postdata interface{}) (*APIResult, error) {
        if postdata == nil {
                return nil, fmt.Errorf("postdata is nil")
        }
        jsondata, err := toJSON(postdata)
        if err != nil {
                return nil, err
        }

        resp, err := j.makeRequest(http.MethodPost, apiurl, jsondata)
        if err != nil {
                return nil, err
        }

        defer resp.Body.Close()
        res := APIResult{}
        json.NewDecoder(resp.Body).Decode(&res)
        return &res, nil
}

//makeRequest makes http request for given url with given method
// also inject Authorization Header
func (j *JwtAPI) makeRequest(method, apiurl string, body io.Reader) (*http.Response, error) {
        retry := 0
        //Create []byte buffer from body - so it can be passed in further retries
        var buf []byte
        if body != nil {
                buf, _ = ioutil.ReadAll(body)
        }

        r, err := http.NewRequest(method, apiurl, bytes.NewReader(buf))
        if err != nil {
                return nil, err
        }
        r.Header.Set("Authorization", "bearer "+j.token.AccessToken)
        r.Header.Set("Content-Type", "application/json")

        client := j.getClient()
        resp, err := client.Do(r)
        if err != nil {
                return nil, err
        }
        return resp, nil
}

func (j *JwtAPI) getClient() *http.Client {
        // default timeout (if not set by client)
        timeoutInSec := 10
        if j.Timeout.Seconds() > 0 {
                // client sets timeout, so use it
                timeoutInSec = int(j.Timeout.Seconds())
        }

        client := &http.Client{
                Timeout: time.Second * time.Duration(timeoutInSec),
        }
        return client
}

1 个答案:

答案 0 :(得分:0)

为了使您的代码更具弹性,您应该添加一些带有后退的重试,因此即使连接被拒绝,它仍然有效。

拒绝连接意味着未打开端口。中间是否有防火墙或代理?认证部分在这里不重要,因为它甚至没有达到这一点。

您可以检查的一些事项:

  • 确保服务正在运行
  • 检查防火墙配置
  • 实施弹性重试
  • IP地址是否已修复?动态DNS是否已使用,可能未更新?

退避重试的套餐

至于实施后退,你可以试试这个包:

https://github.com/cenkalti/backoff

列出了如何使用它的示例,它正是您所需要的:

// An operation that may fail.
operation := func() error {
    // do the request here and check the response code (or check the response body depending on your need) . e.g. above 500 should retry, above 400 and below 500, it should be a client side error and retrying might not help much
    return nil // or an error
}

err := Retry(operation, NewExponentialBackOff())
if err != nil {
    // Handle error.
    return
}

// Operation is successful.