Spring-Retry

關於重試

  開發中常常要調用其餘項目提供的Api,這些Api多是同公司其餘團隊或其餘公司提供的,因此避免不了會出現網絡抖動調用失敗的狀況,這種狀況下每每重試一次就正常了。對於add或update操做,若是是非冪等性操做,要謹慎使用,重試可能會形成業務異常。java

能夠使用apache HttpClient 或本身實現,spring 也提供了重試機制 Spring Retry。git

GitHub地址: Spring-Retrygithub

有兩種使用方式,基於編碼方式和基於申明式,下面是經常使用類圖:web

經常使用類圖結構

補償/回退 策略

重試策略spring

重試上下文緩存(強引用 與 軟引用 緩存方式)express

重試監聽者(實現不一樣階段的通知功能)apache

引入依賴

            <dependency>
                <groupId>org.springframework.retry</groupId>
                <artifactId>spring-retry</artifactId>
            </dependency>

編碼方式

RetryTemplate配置

    @Bean(name = "retryTemplate")
    public RetryTemplate getRetryTemplate() {
        RetryTemplate template = new RetryTemplate();

        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        //設置2s重試一次
        backOffPolicy.setBackOffPeriod(2000);
        template.setBackOffPolicy(backOffPolicy);

        SoftReferenceMapRetryContextCache contextCache = new SoftReferenceMapRetryContextCache();
        template.setRetryContextCache(contextCache);

        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        template.setRetryPolicy(retryPolicy);

        template.setThrowLastExceptionOnExhausted(true);
        return template;
    }

不指定 RecoveryCallback 

    @Autowired RetryTemplate retryTemplate;
@RequestMapping(value
= "/test", method = { RequestMethod.GET }) public void test() { Boolean execute = retryTemplate.execute(ctx -> testMethod("arg-1", 12)); System.out.println("result : " + execute); } public Boolean testMethod(String arg1, Integer arg2) throws RuntimeException { logger.info("調用 testMethod 方法:arg1:" + arg1 + ",arg2:" + arg2); throw new RuntimeException("出錯啦!"); }

執行結果:緩存

2019-08-03 18:23:19.557 [INFO][http-nio-8097-exec-1]:c.demo.controller.DemoController [testMethod:65] 調用 testMethod 方法:arg1:arg-1,arg2:12
2019-08-03 18:23:21.563 [INFO][http-nio-8097-exec-1]:c.demo.controller.DemoController [testMethod:65] 調用 testMethod 方法:arg1:arg-1,arg2:12
2019-08-03 18:23:23.563 [INFO][http-nio-8097-exec-1]:c.demo.controller.DemoController [testMethod:65] 調用 testMethod 方法:arg1:arg-1,arg2:12
2019-08-03 18:23:23.567 [ERROR][http-nio-8097-exec-1]:com.demo.CommonExceptionAdvice [processUnauthenticatedException:45] 
java.lang.RuntimeException: 出錯啦!

指定 RecoveryCallback 

    @RequestMapping(value = "/test", method = { RequestMethod.GET })
    public void test() {
        Boolean execute = retryTemplate.execute(ctx -> testMethod("arg-1", 12), ctx -> callBack("arg-1", 12));
        System.out.println("result : " + execute);
    }

    public Boolean testMethod(String arg1, Integer arg2) throws RuntimeException {
        logger.info("調用 testMethod 方法:arg1:" + arg1 + ",arg2:" + arg2);
        throw new RuntimeException("出錯啦!");
    }

    public Boolean callBack(String arg1, Integer arg2) throws RuntimeException {
        logger.info("調用 callBack 方法:arg1:" + arg1 + ",arg2:" + arg2);
        return false;
    }

返回結果: 網絡

2019-08-03 18:39:23.777 [INFO][http-nio-8097-exec-1]:c.demo.controller.DemoController [testMethod:63] 調用 testMethod 方法:arg1:arg-1,arg2:12
2019-08-03 18:39:25.781 [INFO][http-nio-8097-exec-1]:c.demo.controller.DemoController [testMethod:63] 調用 testMethod 方法:arg1:arg-1,arg2:12
2019-08-03 18:39:27.781 [INFO][http-nio-8097-exec-1]:c.demo.controller.DemoController [testMethod:63] 調用 testMethod 方法:arg1:arg-1,arg2:12
2019-08-03 18:39:27.781 [INFO][http-nio-8097-exec-1]:c.demo.controller.DemoController [callBack:68] 調用 callBack 方法:arg1:arg-1,arg2:12
result : false 

註解申明

@Retryable註解 

註解參數:

value:指定發生的異常進行重試
include:和value同樣,默認空,當exclude也爲空時,全部異常都重試
exclude:指定異常不重試,默認空,當include也爲空時,全部異常都重試
maxAttemps:重試次數,默認3
backoff:重試補償機制,默認沒有 app

    /**
     * Exception types that are retryable. Synonym for includes(). Defaults to empty (and
     * if excludes is also empty all exceptions are retried).
     * @return exception types to retry
     */
    Class<? extends Throwable>[] value() default {};

    /**
     * Exception types that are retryable. Defaults to empty (and if excludes is also
     * empty all exceptions are retried).
     * @return exception types to retry
     */
    Class<? extends Throwable>[] include() default {};

    /**
     * Exception types that are not retryable. Defaults to empty (and if includes is also
     * empty all exceptions are retried).
     * @return exception types to retry
     */
    Class<? extends Throwable>[] exclude() default {};

    /**
     * @return the maximum number of attempts (including the first failure), defaults to 3
     */
    int maxAttempts() default 3;

    /**
     * Specify the backoff properties for retrying this operation. The default is no
     * backoff, but it can be a good idea to pause between attempts (even at the cost of
     * blocking a thread).
     * @return a backoff specification
     */
    Backoff backoff() default @Backoff();
View Code

@Backoff

value:和delay同樣,在指數級狀況下用做初始值
maxDelay:兩次重試之間的最大時間若是小於delay,則忽略
multiplier:若是爲正數,用來做爲生成下一次補償的延遲時間乘數,默認值0表示忽略(eg:2, 第一次 delay * 2 第二次 delay * 2 * 2)

    /**
     * Synonym for {@link #delay()}.
     *
     * @return the delay in milliseconds (default 1000)
     */
    long value() default 1000;

    /**
     * A canonical backoff period. Used as an initial value in the exponential case, and
     * as a minimum value in the uniform case.
     * @return the initial or canonical backoff period in milliseconds (default 1000)
     */
    long delay() default 0;

    /**
     * The maximimum wait (in milliseconds) between retries. If less than the
     * {@link #delay()} then ignored.
     *
     * @return the maximum delay between retries (default 0 = ignored)
     */
    long maxDelay() default 0;

    /**
     * If positive, then used as a multiplier for generating the next delay for backoff.
     *
     * @return a multiplier to use to calculate the next backoff delay (default 0 =
     * ignored)
     */
    double multiplier() default 0;

    /**
     * An expression evaluating to the canonical backoff period. Used as an initial value
     * in the exponential case, and as a minimum value in the uniform case.
     * Overrides {@link #delay()}.
     * @return the initial or canonical backoff period in milliseconds.
     * @since 1.2
     */
    String delayExpression() default "";

    /**
     * An expression evaluating to the maximimum wait (in milliseconds) between retries.
     * If less than the {@link #delay()} then ignored.
     * Overrides {@link #maxDelay()}
     *
     * @return the maximum delay between retries (default 0 = ignored)
     * @since 1.2
     */
    String maxDelayExpression() default "";

    /**
     * Evaluates to a vaule used as a multiplier for generating the next delay for backoff.
     * Overrides {@link #multiplier()}.
     *
     * @return a multiplier expression to use to calculate the next backoff delay (default 0 =
     * ignored)
     * @since 1.2
     */
    String multiplierExpression() default "";

    /**
     * In the exponential case ({@link #multiplier()} &gt; 0) set this to true to have the
     * backoff delays randomized, so that the maximum delay is multiplier times the
     * previous delay and the distribution is uniform between the two values.
     *
     * @return the flag to signal randomization is required (default false)
     */
    boolean random() default false;
View Code

示例 

在主類上加上@EnableRetry註解,表示啓用重試機制。

@EnableRetry
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

指定重試異常 和 重試指數 

    public Boolean testMethod(String arg1, Integer arg2) throws RuntimeException {
        logger.info("調用 testMethod 方法:arg1:" + arg1 + ",arg2:" + arg2);
        throw new RuntimeException("出錯啦!");
    }

    @RequestMapping(value = "/testAn/{arg1}/{arg2}", method = { RequestMethod.GET })
    @Retryable(value = {Exception.class, RuntimeException.class}, backoff = @Backoff(multiplier = 1.5))
    public Boolean testExe(@PathVariable("arg1") String arg1, @PathVariable("arg2") Integer arg2) {
        Boolean result = testMethod(arg1, arg2);
        System.out.println("result : " + result);
        return result;
    }

    @Recover
    public Boolean backOffMethod(RuntimeException ex, String arg1, Integer arg2) {
        logger.info("調用 backOffMethod 方法:arg1:" + arg1 + ",arg2:" + arg2);
        return false;
    }

重試結果 

2019-08-07 15:24:31.790 [INFO][http-nio-8097-exec-8]:c.ppdai.koocapp.web.controller.SysCheckController [testMethod:51] [eca27f812e304ec0964ddff868c1c98c] 調用 testMethod 方法:arg1:test-annotation,arg2:12
2019-08-07 15:24:32.791 [INFO][http-nio-8097-exec-8]:c.ppdai.koocapp.web.controller.SysCheckController [testMethod:51] [eca27f812e304ec0964ddff868c1c98c] 調用 testMethod 方法:arg1:test-annotation,arg2:12
2019-08-07 15:24:34.291 [INFO][http-nio-8097-exec-8]:c.ppdai.koocapp.web.controller.SysCheckController [testMethod:51] [eca27f812e304ec0964ddff868c1c98c] 調用 testMethod 方法:arg1:test-annotation,arg2:12
2019-08-07 15:24:34.292 [INFO][http-nio-8097-exec-8]:c.ppdai.koocapp.web.controller.SysCheckController [backOffMethod:71] [eca27f812e304ec0964ddff868c1c98c] 調用 backOffMethod 方法:arg1:test-annotation,arg2:12
相關文章
相關標籤/搜索