前序
马上过年了,预祝大家,新年快乐,少写bug
什么是spring retry?
spring retry是从spring batch独立出来的一个能功能,主要实现了重试和熔断。
什么时候用?
远程调用超时、网络突然中断可以重试。对于重试是有场景限制的,不是什么场景都适合重试,比如参数校验不合法、写操作等(要考虑写是否幂等)都不适合重试。
怎么用?
1,首先我们新建一个maven工程(如果不会,请移步 http://www.cnblogs.com/JJJ1990/p/8384386.html,大佬请忽略),然后在pom文件中引入spring retry 的jar包,代码如下:
1 <!-- spring-retry重试机制 --> 2 <dependency> 3 <groupId>org.springframework.retry</groupId> 4 <artifactId>spring-retry</artifactId> 5 <version>1.1.2.RELEASE</version> 6 </dependency>
2,编写重试部分代码,我们直接在app类的main方法中实现
首先我们先制定好重试策略,也就是当异常发生后,我们重试几次,每次间隔多久等
如下代码中,第一行为新建一个重试模板,第二行为制定一个简单重试策略,特别注意最后的数字3,这就是我们设置的要重试的次数
1 final RetryTemplate retryTemplate = new RetryTemplate(); 2 final SimpleRetryPolicy policy = new SimpleRetryPolicy(3, 3 Collections.<Class<? extends Throwable>, 4 Boolean>singletonMap(Exception.class, true));
3,下面再设置退避策略,注意第二行 2000为每次间隔的时间,单位ms,然后再将 重试策略和退避策略设置到重试模板中如第3.4行
1 FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); 2 fixedBackOffPolicy.setBackOffPeriod(2000); 3 retryTemplate.setRetryPolicy(policy); 4 retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
4,编写重试业务部分代码如下,主要是实现RetryCallback接口的 doWithRetry 方法,在这个里面就是编写主要的业务逻辑了。我在这块模拟了一个异常,通过数组下标越界异常,来进行重试
1 final RetryCallback<String, Exception> retryCallback = new RetryCallback<String, Exception>() { 2 public String doWithRetry(RetryContext context) throws Exception { 3 System.out.println(new Date()); 4 System.out.println("retryCallback"); 5 String [] str = new String [2]; 6 str[3] = ""; 7 return "1"; 8 } 9 };
5,编写恢复回调代码如下,也是实现了RecoveryCallback接口中的recover方法,这个方法的作用就是当第4步重试代码按照重试策略执行完毕后,依旧异常,那就会执行下面的代码,这样你就可以通过下面的代码,来规避异常
1 final RecoveryCallback<String> recoveryCallback = new RecoveryCallback<String>() { 2 public String recover(RetryContext context) throws Exception { 3 System.out.println("recoveryCallback"); 4 return null; 5 } 6 };
6,编写重试模板执行重试代码及恢复回调代码
1 try { 2 System.out.println("retryTemplate execute start"); 3 String response = retryTemplate.execute(retryCallback, recoveryCallback); 4 System.out.println("retryTemplate execute end"); 5 } catch (Exception e) { 6 e.printStackTrace(); 7 }
测试代码
启动程序,注意整个代码是全部在main方法中实现的,我们直接启动程序看打印的日志信息,从日志信息中可以得知,我们在启动程序后先进入execute方法,然后执行retrycallback,但是每次执行的时候都有数组下标越觉异常,所以他就重试了3次,而且每次的时间间隔是我们设置的2秒,当第三次执行失败后,就调用recovercallback方法,然后整个程序结束。
退别策略有哪些?
1,我们上面的代码中用的退别策略是固定时间间隔,还有其他几种的退避策略大致如下:
-
NoBackOffPolicy:无退避算法策略,每次重试时立即重试
-
FixedBackOffPolicy:固定时间的退避策略,需设置参数sleeper和backOffPeriod,sleeper指定等待策略,默认是Thread.sleep,即线程休眠,backOffPeriod指定休眠时间,默认1秒
-
UniformRandomBackOffPolicy:随机时间退避策略,需设置sleeper、minBackOffPeriod和maxBackOffPeriod,该策略在[minBackOffPeriod,maxBackOffPeriod之间取一个随机休眠时间,minBackOffPeriod默认500毫秒,maxBackOffPeriod默认1500毫秒
-
ExponentialBackOffPolicy:指数退避策略,需设置参数sleeper、initialInterval、maxInterval和multiplier,initialInterval指定初始休眠时间,默认100毫秒,maxInterval指定最大休眠时间,默认30秒,multiplier指定乘数,即下一次休眠时间为当前休眠时间*multiplier
-
ExponentialRandomBackOffPolicy:随机指数退避策略,引入随机乘数可以实现随机乘数回退
我们将第3步的代码进行修改,如下:
1 ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy(); 2 exponentialBackOffPolicy.setInitialInterval(2000); 3 exponentialBackOffPolicy.setMultiplier(3); 4 exponentialBackOffPolicy.setMaxInterval(5000); 5 retryTemplate.setRetryPolicy(policy); 6 retryTemplate.setBackOffPolicy(exponentialBackOffPolicy);
上述代码中,2000为执行的时间间隔,3为倍数,5000为允许的最大间隔时间,执行代码结果如下:
如上图,第一次执行和第二次执行间隔2秒,第二次和第三次的间隔本来是2*3=6秒,但是由于设置了最大间隔所以在5秒的时候就触发了重试
重试业务中的异常不要捕获
在我们的重试业务代码中我们需要根据异常来进行重试,如果你在业务代码中捕获了异常会怎么样??我们修改下第4步代码看看:
1 final RetryCallback<String, Exception> retryCallback = new RetryCallback<String, Exception>() { 2 public String doWithRetry(RetryContext context) throws Exception { 3 System.out.println(new Date()); 4 System.out.println("retryCallback"); 5 try { 6 String [] str = new String [2]; 7 str[3] = ""; 8 } catch (Exception e) { 9 // TODO: handle exception 10 } 11 12 return "1"; 13 } 14 };
如上,很简单,我们直接将数组异常try catch ,然后运行代码结果如下
如上图,从信息中可以看出,本来异常的代码只执行了一次,而且没有调用恢复回调代码。
所以如果你需要执行重试,那么就不要捕获你需要重试的异常信息。
所以如果你需要执行重试,那么就不要捕获你需要重试的异常信息。
所以如果你需要执行重试,那么就不要捕获你需要重试的异常信息。
重要的话说三遍~~~