開發中常常要調用其餘項目提供的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>
@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; }
@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: 出錯啦!
@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
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();
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()} > 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;
在主類上加上@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