github.com/qiniu/go-sdk/v7@v7.27.0/internal/clientv2/interceptor_retry_hosts.go (about)

     1  package clientv2
     2  
     3  import (
     4  	"io"
     5  	"net/http"
     6  	"net/url"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/qiniu/go-sdk/v7/internal/hostprovider"
    11  	internal_io "github.com/qiniu/go-sdk/v7/internal/io"
    12  	"github.com/qiniu/go-sdk/v7/storagev2/retrier"
    13  )
    14  
    15  type HostsRetryConfig struct {
    16  	RetryMax           int // 最大重试次数
    17  	ShouldRetry        func(req *http.Request, resp *http.Response, err error) bool
    18  	Retrier            retrier.Retrier           // 重试器
    19  	HostFreezeDuration time.Duration             // 主备域名冻结时间(默认:600s),当一个域名请求失败被冻结的时间,最小 time.Millisecond
    20  	HostProvider       hostprovider.HostProvider // 备用域名获取方法
    21  	ShouldFreezeHost   func(req *http.Request, resp *http.Response, err error) bool
    22  }
    23  
    24  func (c *HostsRetryConfig) init() {
    25  	if c.RetryMax < 0 {
    26  		c.RetryMax = 1
    27  	}
    28  
    29  	if c.HostFreezeDuration < time.Millisecond {
    30  		c.HostFreezeDuration = 600 * time.Second
    31  	}
    32  
    33  	if c.ShouldFreezeHost == nil {
    34  		c.ShouldFreezeHost = func(req *http.Request, resp *http.Response, err error) bool {
    35  			return true
    36  		}
    37  	}
    38  }
    39  
    40  func (c *HostsRetryConfig) getRetryDecision(req *http.Request, resp *http.Response, err error, attempts int) retrier.RetryDecision {
    41  	if c.ShouldRetry != nil {
    42  		if c.ShouldRetry(req, resp, err) {
    43  			return retrier.RetryRequest
    44  		} else {
    45  			return retrier.DontRetry
    46  		}
    47  	} else if c.Retrier != nil {
    48  		return c.Retrier.Retry(resp, err, &retrier.RetrierOptions{Attempts: attempts})
    49  	} else {
    50  		return errorRetrier.Retry(resp, err, &retrier.RetrierOptions{Attempts: attempts})
    51  	}
    52  }
    53  
    54  type hostsRetryInterceptor struct {
    55  	options HostsRetryConfig
    56  }
    57  
    58  func NewHostsRetryInterceptor(options HostsRetryConfig) Interceptor {
    59  	return &hostsRetryInterceptor{
    60  		options: options,
    61  	}
    62  }
    63  
    64  func (interceptor *hostsRetryInterceptor) Priority() InterceptorPriority {
    65  	return InterceptorPriorityRetryHosts
    66  }
    67  
    68  func (interceptor *hostsRetryInterceptor) Intercept(req *http.Request, handler Handler) (resp *http.Response, err error) {
    69  	if interceptor == nil || req == nil {
    70  		return handler(req)
    71  	}
    72  
    73  	interceptor.options.init()
    74  
    75  	// 不重试
    76  	if interceptor.options.RetryMax <= 0 {
    77  		return handler(req)
    78  	}
    79  
    80  	for i := 0; ; i++ {
    81  		// Clone 防止后面 Handler 处理对 req 有污染
    82  		reqBefore := cloneReq(req)
    83  
    84  		resp, err = handler(req)
    85  
    86  		retryDecision := interceptor.options.getRetryDecision(reqBefore, resp, err, i)
    87  		if retryDecision == retrier.DontRetry {
    88  			return resp, err
    89  		}
    90  
    91  		// 尝试冻结域名
    92  		oldHost := req.URL.Host
    93  		if interceptor.options.ShouldFreezeHost(req, resp, err) {
    94  			if fErr := interceptor.options.HostProvider.Freeze(oldHost, err, interceptor.options.HostFreezeDuration); fErr != nil {
    95  				break
    96  			}
    97  		}
    98  
    99  		if i >= interceptor.options.RetryMax {
   100  			break
   101  		}
   102  
   103  		// 尝试更换域名
   104  		newHost, pErr := interceptor.options.HostProvider.Provider()
   105  		if pErr != nil {
   106  			break
   107  		}
   108  		if index := strings.Index(newHost, "://"); index >= 0 {
   109  			newHost = newHost[(index + len("://")):]
   110  		}
   111  		if index := strings.Index(newHost, "/"); index >= 0 {
   112  			newHost = newHost[:index]
   113  		}
   114  		if len(newHost) == 0 {
   115  			break
   116  		}
   117  
   118  		if newHost != oldHost {
   119  			urlString := req.URL.String()
   120  			urlString = strings.Replace(urlString, oldHost, newHost, 1)
   121  			u, ppErr := url.Parse(urlString)
   122  			if ppErr != nil {
   123  				break
   124  			}
   125  
   126  			reqBefore.Host = u.Host
   127  			reqBefore.URL = u
   128  		}
   129  
   130  		req = reqBefore
   131  
   132  		if req.Body != nil && req.GetBody != nil {
   133  			if closer, ok := req.Body.(io.Closer); ok {
   134  				closer.Close()
   135  			}
   136  			if req.Body, err = req.GetBody(); err != nil {
   137  				return
   138  			}
   139  		}
   140  
   141  		if resp != nil && resp.Body != nil {
   142  			_ = internal_io.SinkAll(resp.Body)
   143  			resp.Body.Close()
   144  		}
   145  	}
   146  	return resp, err
   147  }