0x01 问题描述

在go项目中,使用 resty 。在http请求中,添加重试条件,在满足条件时才可以重试。使用中,后端所连的服务突然挂线,导致端口不存在,进而引发 connect refused 的错误。而又由于设置超时时间为 30s,导致在服务器响应前,请求方已经断开而报超时的问题。

0x02 代码片断
var client = resty.New()

func init() {
	client.SetRetryCount(5)
	client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
	client.SetRetryWaitTime(time.Second * time.Duration(2))
	client.AddRetryCondition(func(response *resty.Response) (b bool, err error) {
		if response == nil || response.StatusCode() == 0 || (response.StatusCode() >= http.StatusLocked && response.StatusCode() < http.StatusNotExtended) {
			return false, nil
		} else {
			return true, nil
		}
	})
}

// GetFromBackServer 从后端服务获取数据
func GetFromBackServer() {
	resp, err := client.R().Post("http://127.0.0.1:8083/v2/hello")
	if err != nil {
		fmt.Printf("err:%s", err.Error())
		return
	}
	fmt.Printf("resp:%+v", resp)
}
response.StatusCodefalse
0x03 跟踪调试
gopkg.in/resty.v1/request.go
	_ = Backoff(
		func() (*Response, error) {
			attempt++

			r.URL = r.selectAddr(addrs, url, attempt)

			resp, err = r.client.execute(r)
			if err != nil {
				r.client.Log.Printf("ERROR %v, Attempt %v", err, attempt)
				if r.isContextCancelledIfAvailable() {
					// stop Backoff from retrying request if request has been
					// canceled by context
					return resp, nil
				}
			}

			return resp, err
		},
		Retries(r.client.RetryCount),
		WaitTime(r.client.RetryWaitTime),
		MaxWaitTime(r.client.RetryMaxWaitTime),
		RetryConditions(r.client.RetryConditions),
	)
Backoffgopkg.in/resty.v1/resty.go
// Backoff retries with increasing timeout duration up until X amount of retries
// (Default is 3 attempts, Override with option Retries(n))
func Backoff(operation func() (*Response, error), options ...Option) error {
	...
	for attempt := 0; attempt < opts.maxRetries; attempt++ {
		resp, err = operation()

		var needsRetry bool
		var conditionErr error
		for _, condition := range opts.retryConditions {
			needsRetry, conditionErr = condition(resp)
			if needsRetry || conditionErr != nil {
				break
			}
		}

		if err == nil && !needsRetry && conditionErr == nil {
			return nil
		}
		
		temp := math.Min(capLevel, base*math.Exp2(float64(attempt)))
		ri := int(temp / 2)
		if ri <= 0 {
			ri = 1<<31 - 1 // max int for arch 386
		}
		sleepDuration := time.Duration(math.Abs(float64(ri + rand.Intn(ri))))

		if sleepDuration < opts.waitTime {
			sleepDuration = opts.waitTime
		}
		time.Sleep(sleepDuration)
	}

	return err
}
forerr
if err == nil && !needsRetry && conditionErr == nil {
	return nil
}

条件不满足,所以一直循环