学习使用Guava Retryer

一、引入

  在平时的开发工作中,重试机制,是一个很重要的逻辑,比如调用其他服务时,如果出现超时,那么可以等100毫秒后再进行调用,或者出现异常时,需要重试;可以重试多次,也可以重试1次,这个都是可以在程序中设定的。

  实现上面的逻辑,最简单的方式就是使用for循环了,示例如下:

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

package cn.ganlixin.guava;

import org.junit.Test;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

public class UseRetryer {

    private static final Logger log = LoggerFactory.getLogger(UseRetryer.class);

    @Test

    public void testUseFor() throws InterruptedException {

        int retryTimes = 3;  // 重试次数

        // 使用for循环控制重试

        for (int i = 0; i < retryTimes; i++) {

            try {

                // 逻辑代码,比如调用其他服务的接口

            catch (Exception e) {

                log.warn("第{}次运行出现异常,", i);

                // 如果出现异常,休眠100毫秒后重试(继续for循环)

                Thread.sleep(100);

                continue;

            }

            // 执行成功,立即终止for循环。

            break;

        }

    }

}

  上面的代码,实现起来很简单,也很好理解,我之前也是这样做的。但是这样做,其实是有一个弊端的,因为业务逻辑代码,和一些控制操作(什么时候重试、隔多久重试)是写在一块的,比较乱。

  本文所讲的Guava Retryer可以解决上面的问题(还有其他优点)。  

二、快速上手

2.1、导入依赖

  Guava Retryer并不属于Guava,他是一个基于Guava,提供重试机制的库。

  guava retryer的github网址:GitHub - rholder/guava-retrying: 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.,包含有使用文档。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

<dependency>

    <groupId>com.google.guava</groupId>

    <artifactId>guava</artifactId>

    <version>28.0-jre</version>

</dependency>

<dependency>

    <groupId>com.github.rholder</groupId>

    <artifactId>guava-retrying</artifactId>

    <version>2.0.0</version>

    <!-- 排除与guava重复的依赖 -->

    <exclusions>

        <exclusion>

            <groupId>com.google.guava</groupId>

            <artifactId>guava</artifactId>

        </exclusion>

        <exclusion>

            <groupId>com.google.code.findbugs</groupId>

            <artifactId>jsr305</artifactId>

        </exclusion>

    </exclusions>

</dependency>

  

2.2、第一个示例

  先提及一下, retryer是一个重试器,可以对重试器进行设置,比如什么情况下重试、隔多久重试、重试多少次...

  创建好重试器后,就可以使用重试器来进行执行指定的操作(实际的业务逻辑):

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

package cn.ganlixin.guava;

import com.github.rholder.retry.*;

import org.junit.Test;

import java.time.LocalTime;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.TimeUnit;

public class UseRetryer {

    // 需要重试执行的操作

    private Boolean testCode() {

        System.out.println(LocalTime.now());

        // 强制抛出异常,触发重试

        if (true) {

            throw new RuntimeException("手动测试抛出异常");

        else {

            return false;

        }

    }

    @Test

    public void testFirstRetryer() throws ExecutionException, RetryException, InterruptedException {

        // 创建一个重试器,重试器执行的方法,返回值为Boolean类型

        Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()

                // 出现异常时,会重试

                .retryIfException()

                // 失败后,隔2秒后重试

                .withWaitStrategy(WaitStrategies.fixedWait(2, TimeUnit.SECONDS))

                // 重试3次后,仍未成功,就不再重试

                .withStopStrategy(StopStrategies.stopAfterAttempt(3))

                .build();

        // 使用重试器,执行具体逻辑

        Boolean res = retryer.call(() -> {

            return testCode();

        });

    }

}

  

  运行程序,输出:

1

2

3

4

5

6

7

8

23:35:37.753

23:35:39.754

23:35:41.759

com.github.rholder.retry.RetryException: Retrying failed to complete successfully after 3 attempts.

..........

Caused by: java.lang.RuntimeException: 手动测试抛出异常

..........

  

三、重试设置

  上面简单介绍了怎么使用guava retryer,但是并没有看出retryer的优点,下面对guava retryer的重试设置进行介绍,比如,什么时候重试,重试多少次,每次充重试间隔多久..然后就可以发现guava retryer在进行重试时,挺“优雅”的。

3.1、重试条件设置

  什么时候执行重试,Guava Retryer有多个匹配方法:

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

package cn.ganlixin.guava;

import com.github.rholder.retry.RetryException;

import com.github.rholder.retry.Retryer;

import com.github.rholder.retry.RetryerBuilder;

import com.github.rholder.retry.StopStrategies;

import org.apache.commons.lang3.StringUtils;

import org.junit.Test;

import java.util.concurrent.ExecutionException;

public class UseRetryer {

    @Test

    public void testWhenRetry() throws ExecutionException, RetryException {

        final Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()

                // 当运行结果大于10的时候,需要重试(表达式返回true,则重试)

                .retryIfResult(res -> {

                    return res > 10;

                })

                // 当抛出异常时,就会进行重试

                .retryIfException()

                // 或者当抛出ArithmeticException异常时,进行重试

                .retryIfExceptionOfType(ArithmeticException.class)

                // 如果抛出RuntimeException异常时,进行重试

                .retryIfRuntimeException()

                // 捕获到异常,对异常进行处理,返回true时,会进行重试

                .retryIfException(exception -> {

                    System.out.println("捕获到" + exception);

                    // 可以对异常信息、异常种类进行处理,决定是否需要重试

                    return StringUtils.contains(exception.getMessage(), "wrong");

                })

                // 重试3次

                .withStopStrategy(StopStrategies.stopAfterAttempt(3))

                .build();

        Integer res = retryer.call(() -> {

            throw new Exception();

        });

        System.out.println(res);

    }

}

  

3.2、重试次数设置

  前面说了什么情况下需要重试,那么需要重试多少次呢?

  guava retryer中,如果没有设置重试多少次,那么将会没有休止地一直重试(死循环),所以建议一定要设置重试次数。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

package cn.ganlixin.guava;

import com.github.rholder.retry.Retryer;

import com.github.rholder.retry.RetryerBuilder;

import com.github.rholder.retry.StopStrategies;

import org.junit.Test;

public class UseRetryer {

    @Test

    public void testRetryStop() {

        Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()

                // 抛出异常时重试

                .retryIfException()

                // 设置什么时候停止重试

                // 这里设置的是,执行3次都失败了就停止重试

                .withStopStrategy(StopStrategies.stopAfterAttempt(3))

                .build();

    }

}

  设置停止策略,主要是使用StopStrategies类的静态方法,如下:

1

2

3

4

5

6

7

8

// 不停止,一直重试(默认)

StopStrategies.neverStop()

// attemptNumber次失败后,停止重试

StopStrategies.stopAfterAttempt(int attemptNumber)

// 执行多久后停止(在未到停止的时间节点前,如果失败,会一致重试)

StopStrategies.stopAfterDelay(2, TimeUnit.SECONDS)

  

3.3、重试间隔设置

  重试时间间隔设置,是指当执行失败后,执行下一次重试,需要等多久,这就是重试间隔策略。

  下面是一个简单示例:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

package cn.ganlixin.guava;

import com.github.rholder.retry.Retryer;

import com.github.rholder.retry.RetryerBuilder;

import com.github.rholder.retry.WaitStrategies;

import org.junit.Test;

import java.util.concurrent.TimeUnit;

public class UseRetryer {

    @Test

    public void testRetryInteval() {

        Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()

                // 出现异常时重试

                .retryIfException()

                // 设置重试时间间隔,此处设置固定时间间隔(2秒)

                .withWaitStrategy(WaitStrategies.fixedWait(2, TimeUnit.SECONDS))

                .build();

    }

}

  和重试次数类似,重试间隔,主要使用WaitStrategies类的静态方法进行设置(有很多方法),常用的如下:

1

2

3

4

5

6

7

8

9

10

// 失败后没有间隔,立即重试(默认)

WaitStrategies.noWait()

// 固定时间间隔(3秒)

WaitStrategies.fixedWait(3, TimeUnit.SECONDS)

// 设置第一次重试的时间间隔,然后后面每次重试时间间隔的增量

incrementingWait(long initialSleepTime, TimeUnit initialSleepTimeUnit, long increment, TimeUnit incrementTimeUnit)

WaitStrategies.incrementingWait(2, TimeUnit.SECONDS, 1, TimeUnit.SECONDS)

// 解释:第一次重试间隔2秒,后面每次间隔时间是在前一个间隔上加1秒(就是3秒),再下一次是4秒

  

转自:学习使用Guava Retryer - 寻觅beyond - 博客园

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值