您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

Guava Retryer轻松实现接口重试

时间:2022-01-04 11:51:53  来源:  作者:小黑说Java

前言

小黑在开发中遇到个问题,我负责的模块需要调用某个三方服务接口查询信息,查询结果直接影响后续业务逻辑的处理;

这个接口偶尔会因网络问题出现超时,导致我的业务逻辑无法继续处理;

这个问题该如何解决呢?,小黑首先想到的就是重试嘛,如果失败了就再调用一次。

问题来了,如果又失败了呢?接着重试嘛。我们循环处理,比如循环5次,全失败则任务服务不可用,结束调用。

如果我又想着5次调用间隔一段时间呢?第一次先隔1秒,然后3秒,然后5秒呢?

小黑发现事情没那么简单,如果自己搞容易出BUG呀。

Guava Retryer轻松实现接口重试

 

转念一想,这个常见挺常见,网上应该有轮子呀,找找看。一不小心就让我给找着啦,哈哈。

Guava Retryer

This is a small extension to google’s Guava library to allow for the creation of configurable retrying strategies for an arbitrary function call, such as something that talks to a remote service with flaky uptime.

使用Guava Retryer你可以自定义来执行重试,同时也可以监控每次重试的结果和行为,最重要的基于 Guava 风格的重试方式真的很方便。

引入依赖

<dependency>
      <groupId>com.github.rholder</groupId>
      <artifactId>guava-retrying</artifactId>
      <version>2.0.0</version>
</dependency>

快速开始

Callable<Boolean> callable = new Callable<Boolean>() {
    public Boolean call() throws Exception {
        return true; // do something useful here
    }
};

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
    .retryIfResult(Predicates.<Boolean>isNull()) // callable返回null时重试
    .retryIfExceptionOfType(IOException.class) // callable抛出IOException重试
    .retryIfRuntimeException() // callable抛出RuntimeException重试
    .withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后停止
    .build();
try {
    retryer.call(callable);
} catch (RetryException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}

在Callable的call()方法返回null,抛出IOException或者RuntimeException时会重试; 将在尝试重试3次后停止,并抛出包含上次失败尝试信息的RetryException; 如果call()方法中弹出任何其他异常,它将被包装并在ExecutionException中重新调用。

指数退避(Exponential Backoff)

根据wiki上对Exponential backoff的说明,指数补偿是一种通过反馈,成倍地降低某个过程的速率,以逐渐找到合适速率的算法。

在以太网中,该算法通常用于冲突后的调度重传。根据时隙和重传尝试次数来决定延迟重传。

在c次碰撞后(比如请求失败),会选择0和2^c - 1之间的随机值作为时隙的数量。

对于第1次碰撞来说,每个发送者将会等待0或1个时隙进行发送。 而在第2次碰撞后,发送者将会等待0到3( 由2^2 -1 计算得到)个时隙进行发送。 而在第3次碰撞后,发送者将会等待0到7( 由2^3 - 1 计算得到)个时隙进行发送。 以此类推……

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
        .retryIfExceptionOfType(IOException.class)
        .retryIfRuntimeException()
        .withWaitStrategy(WaitStrategies.exponentialWait(100, 5, TimeUnit.MINUTES)) // 指数退避
        .withStopStrategy(StopStrategies.neverStop()) // 永远不停止重试
        .build();

创建一个永远重试的重试器,在每次重试失败后以指数级退避间隔递增,直到最多5分钟。5分钟后,从那时起每隔5分钟重试一次。

斐波那契退避(Fibonacci Backoff)

斐波那契数列指的是这样一个数列:

0,1,1,2,3,5,8,13,21,34,55,89...

这个数列从第3项开始,每一项都等于前两项之和。

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
        .retryIfExceptionOfType(IOException.class)
        .retryIfRuntimeException()
        .withWaitStrategy(WaitStrategies.fibonacciWait(100, 2, TimeUnit.MINUTES)) // 斐波那契退避
        .withStopStrategy(StopStrategies.neverStop())
        .build();

创建一个永远重试的重试器,在每次重试失败后以增加斐波那契退避间隔的方式等待,直到最多2分钟。2分钟后,从那时起每隔2分钟重试一次。

与指数退避策略类似,斐波那契退避策略遵循一种模式,即在每次尝试失败后等待的时间越来越长。

对于这两种策略的性能英国利兹大学专门做过性能测试,相比指数退避策略,斐波那契退避策略可能性能更好,吞吐量可能也更好。

重试监听器

当重试发生时,如果需要额外做一些动作,比如发送邮件通知之类的,可以通过RetryListener,Guava Retryer在每次重试之后会自动回调监听器,并且支持注册多个监听。

@Slf4j
class diyRetryListener<Boolean> implements RetryListener {
    @Override
    public <Boolean> void onRetry(Attempt<Boolean> attempt) {
        log.info("重试次数:{}",attempt.getAttemptNumber());
        log.info("距离第一次重试的延迟:{}",attempt.getDelaySinceFirstAttempt());
        if(attempt.hasException()){
            log.error("异常原因:",attempt.getExceptionCause());
        }else {
            System.out.println("正常处理结果:{}" + attempt.getResult());
        }
    }
}

定义监听器之后,需要在Retryer中进行注册。

        Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
                .retryIfResult(Predicates.<Boolean>isNull()) // callable返回null时重试
                .retryIfExceptionOfType(IOException.class) // callable抛出IOException重试
                .retryIfRuntimeException() // callable抛出RuntimeException重试
                .withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后停止
                .withRetryListener(new DiyRetryListener<Boolean>()) // 注册监听器
                .build();

小结

Guava Retryer不光在重试策略上支持多种选择,并且将业务逻辑的处理放在Callable中,和重试处理逻辑分开,实现了解耦,这比小黑自己去写循环处理要优秀太多啦,Guava确实强大。



Tags:接口重试   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
前言小黑在开发中遇到个问题,我负责的模块需要调用某个三方服务接口查询信息,查询结果直接影响后续业务逻辑的处理;这个接口偶尔会因网络问题出现超时,导致我的业务逻辑无法继续...【详细内容】
2022-01-04  Tags: 接口重试  点击:(0)  评论:(0)  加入收藏
▌简易百科推荐
前言小黑在开发中遇到个问题,我负责的模块需要调用某个三方服务接口查询信息,查询结果直接影响后续业务逻辑的处理;这个接口偶尔会因网络问题出现超时,导致我的业务逻辑无法继续...【详细内容】
2022-01-04  小黑说Java    Tags:接口重试   点击:(0)  评论:(0)  加入收藏
我在会议演讲后经常收到的一个问题很奇怪,不是关于我演讲的内容,而是关于我的Linux桌面环境。人们对这个漂亮的发行版更感兴趣,而不是我刚才做的精彩演示我不是在抱怨,我喜欢我...【详细内容】
2022-01-04  木偶跳舞    Tags:Linux 开发   点击:(3)  评论:(0)  加入收藏
写在前面有时候可能做项目组长,负责一个项目开发,但是工作是内网,也没有公司的版本库权限,那这个时候,我们怎么用处理版本控制,可以用集中式的版本库工具SVN,或者分布式的Git,这里和...【详细内容】
2021-12-31  山河已无恙被注册了    Tags:Git版本库   点击:(6)  评论:(0)  加入收藏
1、什么是YAMLYAML是"YAML Ain&rsquo;t a Markup Language"(YAML不是一种标记语言)的递归缩写。YAML的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)。主要强度...【详细内容】
2021-12-29  编程菌zfn    Tags:YAML   点击:(8)  评论:(0)  加入收藏
本文分为三个等级自顶向下地分析了glibc中内存分配与回收的过程。本文不过度关注细节,因此只是分别从arena层次、bin层次、chunk层次进行图解,而不涉及有关指针的具体操作。前...【详细内容】
2021-12-28  linux技术栈    Tags:glibc   点击:(10)  评论:(0)  加入收藏
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(17)  评论:(0)  加入收藏
程序是如何被执行的&emsp;&emsp;程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(11)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(11)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(9)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(12)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条