服务在请求资源,如果遇到网络异常等情况,导致请求失败,这时需要有个重试机制来继续请求。 常见的做法是重试3次,并随机 sleep 几秒。 业务开发的脚手架,HTTP Client 基本会封装好 retry 方法,请求失败时根据配置自动重试。下面以一个常见的 HTTP Client 为例, 看下它是如何实现请求重试。 最后整理其他一些重试机制的实现。

go-resty 重试机制的实现

先看下 go-resty 在发送 HTTP 请求时, 请求重试的实现:

// Execute method performs the HTTP request with given HTTP method and URL// for current `Request`.// resp, err := client.R().Execute(resty.GET, "http://httpbin.org/get")func (r *Request) Execute(method, url string) (*Response, error) {

var addrs []*net.SRV

var resp *Response

var err error

if r.isMultiPart && !(method == MethodPost || method == MethodPut || method == MethodPatch) {

return nil, fmt.Errorf("multipart content is not allowed in HTTP verb [%v]", method)

}

if r.SRV != nil {

_, addrs, err = net.LookupSRV(r.SRV.Service, "tcp", r.SRV.Domain)

if err != nil {

return nil, err

}

}

r.Method = method

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

if r.client.RetryCount == 0 {

resp, err = r.client.execute(r)

return resp, unwrapNoRetryErr(err)

}

attempt := 0

err = Backoff(

func() (*Response, error) {

attempt++

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

resp, err = r.client.execute(r)

if err != nil {

r.client.log.Errorf("%v, Attempt %v", err, attempt)

}

return resp, err

},

Retries(r.client.RetryCount),

WaitTime(r.client.RetryWaitTime),

MaxWaitTime(r.client.RetryMaxWaitTime),

RetryConditions(r.client.RetryConditions),

)

return resp, unwrapNoRetryErr(err)

}

重试流程

梳理 Execute(method, url) 在请求时的重试流程:如果没有设置重试次数,执行 r.client.execute(r) :直接请求 Request , 返回 Response 和 error。

如果 r.client.RetryCount 不等于0 ,执行 Backoff() 函数

Backoff() 方法接收一个处理函数参数,根据重试策略, 进行 attempt 次网络请求, 同时接收 Retries()、WaitTime()等函数参数

Backoff函数

重点看下 Backoff() 函数做了什么动作。

Backoff()代码如下:

// 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 {

// Defaults opts := Options{

maxRetries: defaultMaxRetries,

waitTime: defaultWaitTime,

maxWaitTime: defaultMaxWaitTime,

retryConditions: []RetryConditionFunc{},

}

for _, o := range options {

o(&opts)

}

var (

resp *Response

err error

)

for attempt := 0; attempt <= opts.maxRetries; attempt++ {

resp, err = operation()

ctx := context.Background()

if resp != nil && resp.Request.ctx != nil {

ctx = resp.Request.ctx

}

if ctx.Err() != nil {

return err

}

err1 := unwrapNoRetryErr(err) // raw error, it used for return users callback. needsRetry := err != nil && err == err1 // retry on a few operation errors by default

for _, condition := range opts.retryConditions {

needsRetry = condition(resp, err1)

if needsRetry {

break

}

}

if !needsRetry {

return err

}

waitTime, err2 := sleepDuration(resp, opts.waitTime, opts.maxWaitTime, attempt)

if err2 != nil {

if err == nil {

err = err2

}

return err

}

select {

case

case

return ctx.Err()

}

}

return err

}

梳理 Backoff() 函数的流程:Backoff() 接收 处理函数 和 可选的 Option 函数(retry optione) 作为参数

默认策略3次重试, 通过 步骤一 预设的 Options, 自定义重试策略

设置请求的 repsonse 和 error 变量

开始进行 opts.maxRetries 次 HTTP 请求:

执行处理函数 (发起 HTTP 请求)

如果返回结果不为空并且 context 不为空,保持 repsonse 的请求上下文。 如果上下文出错, 退出 Backoff() 流程

执行 retryConditions(), 设置检查重试的条件。

根据 needsRetry 判断是否退出流程

通过 sleepDuration()计算 Duration(根据此次请求resp, 等待时间配置,最大超时时间和重试次数算出 sleepDuration。 时间算法相对复杂, 具体参考: Exponential Backoff And Jitter)

等待 waitTime 进行下个重试。 如果请求完成退出流程。

一个简单的 Demo

看具体 HTTP Client (有做过简单封装)的请求:

func getInfo() {

request := client.DefaultClient().

NewRestyRequest(ctx, "", client.RequestOptions{

MaxTries: 3,

RetryWaitTime: 500 * time.Millisecond,

RetryConditionFunc: func(response *resty.Response) (b bool, err error) {

if !response.IsSuccess() {

return true, nil

}

return

},

}).SetAuthToken(args.Token)

resp, err := request.Get(url)

if err != nil {

logger.Error(ctx, err)

return

}

body := resp.Body()

if resp.StatusCode() != 200 {

logger.Error(ctx, fmt.Sprintf("Request keycloak access token failed, messages:%s, body:%s","message", resp.Status(),string(body))),

)

return

}

...

}

根据以上梳理的 go-resty 的请求流程, 因为 RetryCount 大于0,所以会进行重试机制,重试次数为3。然后 request.Get(url) 进入到 Backoff() 流程,此时重试的边界条件是: !response.IsSuccess(), 直到请求成功。

一些其他重试机制的实现

可以看出其实 go-resty 的 重试策略不是很简单, 这是一个完善,可定制化, 充分考虑 HTTP 请求场景下的一个机制, 它的业务属性相对比较重。

再来看看两个常见的 Retry 实现:

实现一

// retry retries ephemeral errors from f up to an arbitrary timeoutfunc retry(f func() (err error, mayRetry bool)) error {

var (

bestErr error

lowestErrno syscall.Errno

start time.Time

nextSleep time.Duration = 1 * time.Millisecond

)

for {

err, mayRetry := f()

if err == nil || !mayRetry {

return err

}

if errno, ok := err.(syscall.Errno); ok && (lowestErrno == 0 || errno < lowestErrno) {

bestErr = err

lowestErrno = errno

} else if bestErr == nil {

bestErr = err

}

if start.IsZero() {

start = time.Now()

} else if d := time.Since(start) + nextSleep; d >= arbitraryTimeout {

break

}

time.Sleep(nextSleep)

nextSleep += time.Duration(rand.Int63n(int64(nextSleep)))

}

return bestErr

}

每次重试等待随机延长的时间, 直到 f() 执行完成 或不再重试。

实现二

func Retry(attempts int, sleep time.Duration, f func() error) (err error) {

for i := 0; ; i++ {

err = f()

if err == nil {

return

}

if i >= (attempts - 1) {

break

}

time.Sleep(sleep)

}

return fmt.Errorf("after %d attempts, last error: %v", attempts, err)

}

对函数重试 attempts 次,每次等待 sleep 时间, 直到 f() 执行完成。

loadingcache 有重试机制吗_重试机制的实现相关推荐

  1. Redis 学习笔记-NoSQL数据库 常用五大数据类型 Redis配置文件介绍 Redis的发布和订阅 Redis_事务_锁机制_秒杀 Redis应用问题解决 分布式锁

    1.NoSQL数据库 1.1 NoSQL数据库概述 NoSQL(NosQL = Not Only sQL ),意即"不仅仅是sQL",泛指非关系型的数据库.NoSQL不依赖业务逻辑 ...

  2. 红茶一杯话Binder(传输机制篇_上)

    2019独角兽企业重金招聘Python工程师标准>>> 红茶一杯话Binder (传输机制篇_上) 侯 亮 1 Binder是如何做到精确打击的? 我们先问一个问题,binder机制 ...

  3. java实现王者荣耀匹配规则,王者荣耀匹配机制解析_王者荣耀匹配隐藏分规则介绍...

    王者荣耀玩的人数越来越多,匹配速度也是非常之快,不过,你可有注意到匹配是有一定规则的?今天小编就来给大家介绍一下王者荣耀匹配机制解析. 王者荣耀的匹配机制至少分为三种,分别是匹配赛匹配机制,赏金赛匹配 ...

  4. python垃圾回收机制原理_如何理解和掌握Python垃圾回收机制?

    在编程世界里,当一个对象失去引用或者离开作用域后,它就会被当做垃圾而被自动清除,这就是垃圾回收机制.在现在的高级语言如Python.Java都使用了垃圾回收机制,不过与Java采用的垃圾收集机制不同, ...

  5. java 反射机制 视频_【视频笔记】Java反射机制笔记

    Java 语言的反射机制 在Java运行时环境中,对于任意一个类,可以知道这个类有哪些属性和方法.对于任意一个对象,可以调用它的任意一个方法. 这种动态获取类的信息以及动态调用对象的方法的功能来自于J ...

  6. 远程过程调用失败_快速失败机制amp;失败安全机制

    这是why技术的第29篇原创文章 之前在写<这道Java基础题真的有坑!我求求你,认真思考后再回答.>这篇文章时,我在8.1小节提到了快速失败和失败安全机制. 但是我发现当我搜索" ...

  7. transformer机制讲解_从发展历史视角解析Transformer:从全连接CNN到Transformer

    Transformer架构在机器学习领域(尤其是NLP里)是一项热门研究,为我们带来了许多重要成果,比如:GPT-2.GPT-3等写稿机器人:第一代GPT及其性能更优越的"继任者" ...

  8. RabbitMQ消息确认机制和消息重发机制

    一.机制 首先我们要知道一条消息的传递过程. 生产者 -> 交换机 ->  队列 我们的生产者生产消息,生产完成的消息发送到交换机,由交换机去把这个消息转发到对应的队列上.这其中我们可能在 ...

  9. 【Transformer 相关理论深入理解】注意力机制、自注意力机制、多头注意力机制、位置编码

    目录 前言 一.注意力机制:Attention 二.自注意力机制:Self-Attention 三.多头注意力机制:Multi-Head Self-Attention 四.位置编码:Positiona ...

最新文章

  1. websphere7 linux 命令行安装(静默安装)
  2. HTML中label的两种使用方法
  3. .Net Petshop详解(二):petshop三层结构之DataTier
  4. 双色球霸主网络问题_霸主–统治和管理API的地方
  5. 《架构之美》学习随笔:设计第一步
  6. (转)Arcgis for js加载天地图
  7. c语言程序设计扫雷游戏实验报告,C语言程序设计扫雷游戏实验报告.pdf
  8. vb.net与FLASH的完美结合
  9. matlab里的svmtrain的输出model里,各参数的含义
  10. 基于物品的协同过滤mysql_百万用户,八十万商品,如何计算基于物品的协同过滤...
  11. AI 时代,程序员从小白到小牛的发展攻略丨今晚直播送机械键盘!
  12. ea211css和cst,djm与ea211发动机对比
  13. 人物抠图(Photoshop技巧系列)
  14. html语言设计表单实例,40多个漂亮的网页表单设计实例_HTML/Xhtml_网页制作
  15. Unity 对接友盟与TalkingData数据平台(简易版)
  16. IOS_3DTouch实现
  17. F1值(F-Measure)、准确率(Precision)、召回率(Recall) 菜鸡版理解
  18. 以本职工作为挡箭牌推托,久而久之,你就只能原地踏步。
  19. 第二空间计算机最新破解,我的J4125黑群晖折腾记 - 软件篇:无需U盘引导及固态硬盘扩充空间容量法...
  20. Linux 下后台运行程序,查看和关闭后台运行程序

热门文章

  1. [JS][C++]两题斐波那契数列:上台阶、triangle
  2. sh计算机,计算机教程:shellII(sh).pdf
  3. Dialog 带白色的边的处理方法
  4. Context.getExternalFilesDir()和Context.getExternalCacheDir()
  5. MAP/CAP信令常见消息
  6. 中国互联网+机器视觉行业商业模式创新与投资机会深度研究报告
  7. corepython第九章:文件和输入输出
  8. C# SQLiteHelper
  9. asp.net mvc3 Razor引擎中@使用规则小记
  10. 信号量,互斥锁,条件变量的联系与区别