重试机制是一种在程序执行过程中出现错误后重新尝试执行程序的一种机制。通过重启软件、重新连接网络、重发请求等方式,来减少程序运行过程中出现的错误,从而提高程序的可靠性。
重试机制通常应用在网络通讯等领域,比如在调用远程接口
时,由于网络波动
等原因导致请求失败,就需要进行重试,这样可以最大限度的提高程序的正常运行率。
封装go重试机制的思路
封装go重试机制需要考虑以下几个点:
定义重试最大次数,包括最大尝试次数、重试时间间隔等参数,这些参数可以根据实际情况进行配置和修改。例如,在请求远程接口时,如果经过3次重试后仍然失败,则应该结束本次请求。
处理重试时的错误类型
重试时可能因为不同原因引发不同的错误,需要考虑在重试机制中处理这些错误,以便进行后续的尝试。
定义重试机制的超时时间
超时时间通常在请求远程服务时用到, 称为超时设置,是指等待服务端返回数据的时间间隔, 超出这个时间间隔将引发错误。在重试机制中,如果进行多次重试后,服务端仍未返回数据,则需要在一定时间内强制结束重试。
对重试过程进行日志记录
在重试机制中,需要记录每次重试的结果和原因,包括成功和失败的情况,这样便于排查问题和进行后续优化。
go重试机制实现
重试机制的实现方案可以分为两种:函数封装和结构体封装
函数封装是一个简单的方式,它将所有重试机制相关的参数都封装在参数里面,以便于用户调用程序,但它缺少高级功能,如日志记录和函数实现。
结构体封装是另外一种封装方式,它将重试机制相关的参数封装在结构体里面,允许用户定义结构体以及修改结构体中的参数,同时可以跟踪重试机制的状态。
在本文中,我们将介绍一种使用结构体进行封装的重试机制,代码实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| package retry
import ( "time" )
type Retry struct { retryCount int retryDelay time.Duration retryTimeout time.Duration retryIntervalFunc func(int) time.Duration }
type RetriableError struct { error error }
func (e RetriableError) Error() string { return e.error.Error() }
func (r *Retry) Retry(fn func() error) error { retryStart := time.Now() var err error retryCount := 0
for { duration := r.retryIntervalFunc(retryCount)
time.Sleep(duration)
err = fn()
if err == nil { return nil }
if _, ok := err.(RetriableError); ok { if retryCount < r.retryCount-1 { retryCount++ continue } break }
if retryStart.Add(r.retryTimeout).Before(time.Now()) { break } } return err }
func NewRetry( retryCount int, retryDelay time.Duration, retryTimeout time.Duration, retryIntervalFunc func(attempt int) time.Duration, ) *Retry { return &Retry{ retryCount: retryCount, retryDelay: retryDelay, retryTimeout: retryTimeout, retryIntervalFunc: retryIntervalFunc, } }
func ExampleRemoteAPICall() { r := NewRetry(5, 1*time.Second, 5*time.Minute, func(attempt int) time.Duration { return r.retryDelay })
resp, err := r.Retry(func() error { r, err := http.NewRequest("GET", "<http://api.example.com>", nil) if err != nil { return RetriableError{error: err} } client := &http.Client{} resp, err := client.Do(r) if err != nil { return RetriableError{error: err} } if resp.StatusCode != http.StatusOK { return RetriableError{error: fmt.Errorf("Unexpected status code (%v)", resp.Status)} } return nil }) }
|
在上面的代码中,我们将Retry结构体进行了封装。结构体中包含了重试最大次数(RetryCount)
、重试间隔(RetryDelay)
、超时时间(RetryTimeout)
和重试时间间隔函数(RetryIntervalFunc)
四个参数,通过 NewRetry()方法进行初始化。
其中RetryIntervalFunc接受一个尝试计数器为参数,并据此返回下一次重试之前的时间间隔。fn 函数是要重试的函数,返回一个错误作为重试方法
的返回值。
Retry方法包括一个无限循环,其中会尝试调用 fn 函数。如果 fn 函数返回 nil,method返回 nil,如果错误是RetriableError,则重试,否则返回错误。
重试方法会在每次重试之间等待指定的延迟时间(RetryDelay),延迟的时间间隔将在RetryIntervalFunc函数中进行计算。