zoukankan      html  css  js  c++  java
  • Spring-Retry

    关于重试

      开发中经常要调用其他项目提供的Api,这些Api可能是同公司其他团队或其他公司提供的,所以避免不了会出现网络抖动调用失败的情况,这种情况下往往重试一次就正常了。对于add或update操作,如果是非幂等性操作,要谨慎使用,重试可能会造成业务异常。

    可以使用apache HttpClient 或自己实现,spring 也提供了重试机制 Spring Retry。

    GitHub地址: Spring-Retry

    有两种使用方式,基于编码方式和基于申明式,下面是常用类图:

    常用类图结构

    补偿/回退 策略

    重试策略

    重试上下文缓存(强引用 与 软引用 缓存方式)

    重试监听者(实现不同阶段的通知功能)

    引入依赖

                <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:重试补偿机制,默认没有 

        /**
         * 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
  • 相关阅读:
    Transaction And Lock--使用资源锁来控制并发
    曲演杂坛--页拆分
    维护建议--文件和文件组
    维护建议--开发设计
    维护建议--服务器磁盘
    维护建议--数据库备份
    TSQL--查找连续登陆用户
    shell脚本传递带有空格的参数的解决方法
    oozie无法识别hadoopHA中的ns1
    ERROR tool.ImportTool: Import failed: java.io.IOException: java.lang.ClassNotFoundException: org.apache.hadoop.hive.conf.HiveConf
  • 原文地址:https://www.cnblogs.com/mr-yang-localhost/p/11247286.html
Copyright © 2011-2022 走看看