网站推广在哪好外贸,搜索引擎优化技术都有哪些,建设银行网站下载中心,有没有专门做名片的网站在平时开发中可能在调用服务时会遇到调用失败的情况#xff0c;在springboot 中retery 机制可以很好的满足我们的开发场景#xff0c;下面举个简单的例子模拟第三方调用。
package com.szhome.web.action;import com.szhome.web.service.ThirdApiService;
import org.spring…在平时开发中可能在调用服务时会遇到调用失败的情况在springboot 中retery 机制可以很好的满足我们的开发场景下面举个简单的例子模拟第三方调用。
package com.szhome.web.action;import com.szhome.web.service.ThirdApiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap;
import java.util.Map;/*** Author caizl* Description TODO* Date 2024/01/10/8:43* Version 1.0*/
Controller
RequestMapping(/api)
public class ThirdApiAction {Autowiredprivate ThirdApiService thirdApiService;RequestMapping(/third)ResponseBodypublic MapString,String callThirdApi(RequestParam String id)throws Exception{System.out.println(开始调用第三方接口);thirdApiService.callThirdApi(id);MapString,String map new HashMap();map.put(code,ok);map.put(msg,执行成功);return map;}}package com.szhome.web.service;import com.google.gson.Gson;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;import java.util.HashMap;
import java.util.Map;/*** Author caizl* Description TODO* Date 2024/01/10/8:44* Version 1.0*/
Service
public class ThirdApiService {/*** value抛出指定异常才会重试* include和value一样默认为空当exclude也为空时默认所有异常* exclude指定不处理的异常* maxAttempts最大重试次数默认3次* backoff重试等待策略* 默认使用BackoffBackoff的value默认为1000L我们设置为2000 以毫秒为单位的延迟默认 1000* multiplier指定延迟倍数默认为0表示固定暂停1秒后进行重试如果把multiplier设置为1.5则第一次重试为2秒第二次为3秒第三次为4.5秒。* return*/Retryable(value { RestClientException.class },//异常时进行重试maxAttempts 3,//最大重试次数backoff Backoff(delay 1000, multiplier 2)//backoff指定了重试间隔的初始延迟和延迟倍数。)public String callThirdApi(String id)throws RestClientException{System.out.println(第三方服务类。。。。start。。。。);MapString,String map new HashMap();Gson gson new Gson();try{map.put(code,1);throw new RestClientException(测试带三方重试接口id);}catch (Exception e){map.put(code,0);throw e;}/*finally {return gson.toJson(map);}*/}Recoverpublic String lastProcess(String id){System.out.println(第三方服务失败后最终执行业务模拟记录日志,id id);return ok;}
}使用起来很简单只需要在引入相关jar,并且在启动的时候进行开启这是springboot 的老套路在我们服务层进行 Retryable 的配置在重试机制完成后我们可以配置一个兜底服务Recover我们可以接收请求参数以此我们后续还可以进行补偿服务的延伸扩展使我们的服务更加的灵活健硕。
以下重试机制的参数说明及相关类使用
Backoff 重试回退策略立即重试还是等待一会再重试 value重试延迟时间单位毫秒默认值1000即默认延迟1秒。当未设置multiplier时表示每隔value的时间重试直到重试次数到达maxAttempts设置的最大允许重试次数。当设置了multiplier参数时该值作为幂运算的初始值。 delay等同value参数两个参数设置一个即可。 maxDelay两次重试间最大间隔时间。当设置multiplier参数后下次延迟时间根据是上次延迟时间乘以 multiplier得出的这会导致两次重试间的延迟时间越来越长该参数限制两次重试的最大间隔时间当间隔时间大于该值时计算出的间隔时间将会被忽略使用上次的重试间隔时间。 multiplier作为乘数用于计算下次延迟时间。公式delay delay * multiplier random是否启用随机退避策略默认false。设置为true时启用退避策略重试延迟时间将是delay和maxDelay间的一个随机数。设置该参数的目的是重试的时候避免同时发起重试请求造成Ddos攻击。 CircuitBreaker include: 指定处理的异常类。默认为空 exclude: 指定不需要处理的异常。默认为空 vaue: 指定要重试的异常。默认为空 maxAttempts: 最大重试次数。默认3次 openTimeout: 配置熔断器打开的超时时间默认5s当超过openTimeout之后熔断器电路变成半打开状态只要有一次重试成功则闭合电路 resetTimeout: 配置熔断器重新闭合的超时时间默认20s超过这个时间断路器关闭
Service
class BusinessService {Recoverpublic int fallback(BoomException ex) {return 2;}CircuitBreaker(include Exception.class, openTimeout 20000L, resetTimeout 5000L, maxAttempts 1)public int desireNumber() throws Exception {System.out.println(calling desireNumber());if (Math.random() .5) {throw new Exception(error);}return 1;}
}
RetryTemplate 什么时候使用RetryTemplate
不使用spring容器的时候使用了RetryableCircuitBreaker的方法不能在本类被调用不然重试机制不会生效。也就是要标记为Service然后在其它类使用Autowired注入或者Bean去实例才能生效。 需要使用复杂策略机制和异常场景时 使用有状态重试,且需要全局模式时建议使用 需要使用监听器Listener的场景 需要使用Retry统计分析 RetryPolicy 重试策略 NeverRetryPolicy只允许调用RetryCallback一次不允许重试 AlwaysRetryPolicy允许无限重试直到成功此方式逻辑不当会导致死循环 SimpleRetryPolicy固定次数重试策略默认重试最大次数为3次RetryTemplate默认使用的策略 TimeoutRetryPolicy超时时间重试策略默认超时时间为1秒在指定的超时时间内允许重试 CircuitBreakerRetryPolicy有熔断功能的重试策略需设置3个参数openTimeout、resetTimeout和delegate稍后详细介绍该策略 CompositeRetryPolicy组合重试策略有两种组合方式乐观组合重试策略是指只要有一个策略允许重试即可以悲观组合重试策略是指只要有一个策略不允许重试即可以但不管哪种组合方式组合中的每一个策略都会执行。 BackOffPolicy 退避策略 NoBackOffPolicy无退避算法策略即当重试时是立即重试 FixedBackOffPolicy固定时间的退避策略需设置参数sleeper和backOffPeriodsleeper指定等待策略默认是Thread.sleep即线程休眠backOffPeriod指定休眠时间默认1秒 UniformRandomBackOffPolicy随机时间退避策略需设置sleeper、minBackOffPeriod和maxBackOffPeriod该策略在[minBackOffPeriod,maxBackOffPeriod之间取一个随机休眠时间minBackOffPeriod默认500毫秒maxBackOffPeriod默认1500毫秒 ExponentialBackOffPolicy指数退避策略需设置参数sleeper、initialInterval、maxInterval和multiplierinitialInterval指定初始休眠时间默认100毫秒maxInterval指定最大休眠时间默认30秒multiplier指定乘数即下一次休眠时间为当前休眠时间*multiplier ExponentialRandomBackOffPolicy随机指数退避策略引入随机乘数之前说过固定乘数可能会引起很多服务同时重试导致DDos使用随机休眠时间来避免这种情况。 DEMO RetryTemplate TimeoutRetryPolicy TimeoutRetryPolicy策略TimeoutRetryPolicy超时时间默认是1秒。 TimeoutRetryPolicy超时是指在execute方法内部从open操作开始到调用TimeoutRetryPolicy的canRetry方法这之间所经过的时间。 这段时间未超过TimeoutRetryPolicy定义的超时时间那么执行操作否则抛出异常。 当重试执行完闭操作还未成为那么可以通过RecoveryCallback完成一些失败事后处理。 public class RetryTemplate01 { public static void main(String[] args) throws Exception { RetryTemplate template new RetryTemplate(); TimeoutRetryPolicy policy new TimeoutRetryPolicy(); template.setRetryPolicy(policy); String result template.execute(new RetryCallbackString, Exception() { public String doWithRetry(RetryContext arg0) throws Exception { return Retry; } }); System.out.println(result); } } SimpleRetryPolicy 代码重试两次后仍然失败RecoveryCallback被调用返回”recovery callback”。如果没有定义RecoveryCallback那么重试2次后将会抛出异常。
public class RetryTemplate02 { public static void main(String[] args) throws Exception { RetryTemplate template new RetryTemplate(); SimpleRetryPolicy policy new SimpleRetryPolicy(); policy.setMaxAttempts(2); template.setRetryPolicy(policy); String result template.execute(new RetryCallbackString, Exception() { public String doWithRetry(RetryContext arg0) throws Exception { throw new NullPointerException(nullPointerException); } }, new RecoveryCallbackString() { public String recover(RetryContext context) throws Exception { return recovery callback; } }); System.out.println(result); } } 该策略定义了对指定的异常进行若干次重试。默认情况下对Exception异常及其子类重试3次。 如果创建SimpleRetryPolicy并指定重试异常map可以选择性重试或不进行重试。下面的代码定义了对TimeOutException进行重试。
public class RetryTemplate03 { public static void main(String[] args) throws Exception { RetryTemplate template new RetryTemplate(); MapClass? extends Throwable, Boolean maps new HashMapClass? extends Throwable, Boolean(); maps.put(TimeoutException.class, true); SimpleRetryPolicy policy2 new SimpleRetryPolicy(2, maps); template.setRetryPolicy(policy2); String result template.execute(new RetryCallbackString, Exception() { public String doWithRetry(RetryContext arg0) throws Exception { throw new TimeoutException(TimeoutException); } }, new RecoveryCallbackString() { public String recover(RetryContext context) throws Exception { return recovery callback; } }); System.out.println(result); }
} ExceptionClassifierRetryPolicy 通过PolicyMap定义异常及其重试策略。下面的代码在抛出NullPointerException采用NeverRetryPolicy策略而TimeoutException采用AlwaysRetryPolicy。
public class RetryTemplate04 { public static void main(String[] args) throws Exception { RetryTemplate template new RetryTemplate(); ExceptionClassifierRetryPolicy policy new ExceptionClassifierRetryPolicy(); MapClass? extends Throwable, RetryPolicy policyMap new HashMapClass? extends Throwable, RetryPolicy(); policyMap.put(TimeoutException.class, new AlwaysRetryPolicy()); policyMap.put(NullPointerException.class, new NeverRetryPolicy()); policy.setPolicyMap(policyMap); template.setRetryPolicy(policy); String result template.execute(new RetryCallbackString, Exception() { public String doWithRetry(RetryContext arg0) throws Exception { if (arg0.getRetryCount() 2) { Thread.sleep(1000); throw new NullPointerException(); } throw new TimeoutException(TimeoutException); } }, new RecoveryCallbackString() { public String recover(RetryContext context) throws Exception { return recovery callback; } }); System.out.println(result); }
} CompositeRetryPolicy 用户指定一组策略随后根据optimistic选项来确认如何重试。 下面的代码中创建CompositeRetryPolicy策略并创建了RetryPolicy数组数组有两个具体策略SimpleRetryPolicy与AlwaysRetryPolicy。 当CompositeRetryPolicy设置optimistic为true时Spring-retry会顺序遍历RetryPolicy[]数组如果有一个重试策略可重试例如SimpleRetryPolicy没有达到重试次数那么就会进行重试。 如果optimistic选项设置为false。那么有一个重试策略无法重试那么就不进行重试。 例如SimpleRetryPolicy达到重试次数不能再重试而AlwaysRetryPolicy可以重试那么最终是无法重试的。 下面代码设置setOptimistic(true)而AlwaysRetryPolicy一直可重试那么最终可以不断进行重试。 public class RetryTemplate05 { public static void main(String[] args) throws Exception { RetryTemplate template new RetryTemplate(); CompositeRetryPolicy policy new CompositeRetryPolicy(); RetryPolicy[] polices { new SimpleRetryPolicy(), new AlwaysRetryPolicy() }; policy.setPolicies(polices); policy.setOptimistic(true); template.setRetryPolicy(policy); String result template.execute(new RetryCallbackString, Exception() { public String doWithRetry(RetryContext arg0) throws Exception { if (arg0.getRetryCount() 2) { Thread.sleep(1000); throw new NullPointerException(); } throw new TimeoutException(TimeoutException); } }, new RecoveryCallbackString() { public String recover(RetryContext context) throws Exception { return recovery callback; } }); System.out.println(result); }
} ExponentialRandomBackOffPolicy 通过监听器可以在重试操作的某些位置嵌入调用者定义的一些操作以便在某些场景触发。 代码注册了两个ListenerListener中的三个实现方法onError,open,close会在执行重试操作时被调用 在RetryTemplate中doOpenInterceptors,doCloseInterceptors,doOnErrorInterceptors会调用监听器对应的open,close,onError方法。 doOpenInterceptors方法在第一次重试之前会被调用如果该方法返回true则会继续向下直接如果返回false则抛出异常停止重试。 doCloseInterceptors 会在重试操作执行完毕后调用。 doOnErrorInterceptors 在抛出异常后执行 当注册多个Listener时open方法按会按Listener的注册顺序调用而onError和close则按Listener注册的顺序逆序调用。 public class RetryTemplate06 { public static void main(String[] args) throws Exception { RetryTemplate template new RetryTemplate(); ExponentialRandomBackOffPolicy exponentialBackOffPolicy new ExponentialRandomBackOffPolicy(); exponentialBackOffPolicy.setInitialInterval(1500); exponentialBackOffPolicy.setMultiplier(2); exponentialBackOffPolicy.setMaxInterval(6000); CompositeRetryPolicy policy new CompositeRetryPolicy(); RetryPolicy[] polices { new SimpleRetryPolicy(), new AlwaysRetryPolicy() }; policy.setPolicies(polices); policy.setOptimistic(true); template.setRetryPolicy(policy); template.setBackOffPolicy(exponentialBackOffPolicy); template.registerListener(new RetryListener() { public T, E extends Throwable boolean open(RetryContext context, RetryCallbackT, E callback) { System.out.println(open); return true; } public T, E extends Throwable void onError(RetryContext context, RetryCallbackT, E callback, Throwable throwable) { System.out.println(onError); } public T, E extends Throwable void close(RetryContext context, RetryCallbackT, E callback, Throwable throwable) { System.out.println(close); } }); template.registerListener(new RetryListener() { public T, E extends Throwable boolean open(RetryContext context, RetryCallbackT, E callback) { System.out.println(open2); return true; } public T, E extends Throwable void onError(RetryContext context, RetryCallbackT, E callback, Throwable throwable) { System.out.println(onError2); } public T, E extends Throwable void close(RetryContext context, RetryCallbackT, E callback, Throwable throwable) { System.out.println(close2); } }); String result template.execute(new RetryCallbackString, Exception() { public String doWithRetry(RetryContext arg0) throws Exception { arg0.getAttribute(); if (arg0.getRetryCount() 2) { throw new NullPointerException(); } throw new TimeoutException(TimeoutException); } }); System.out.println(result); }
} 有状态RetryTemplate 当把状态放入缓存时通过该key查询获取全局模式 DataAccessException进行回滚
public class RetryTemplate07 { public static void main(String[] args) throws Exception { RetryTemplate template new RetryTemplate(); Object key mykey; boolean isForceRefresh true; BinaryExceptionClassifier rollbackClassifier new BinaryExceptionClassifier( Collections.Class? extends Throwablesingleton(DataAccessException.class)); RetryState state new DefaultRetryState(key, isForceRefresh, rollbackClassifier); String result template.execute(new RetryCallbackString, RuntimeException() { Override public String doWithRetry(RetryContext context) throws RuntimeException { System.out.println(retry count: context.getRetryCount()); throw new TypeMismatchDataAccessException(); } }, new RecoveryCallbackString() { Override public String recover(RetryContext context) throws Exception { return default; } }, state); System.out.println(result); } } 熔断器场景。在有状态重试时且是全局模式不在当前循环中处理重试而是全局重试模式不是线程上下文如熔断器策略时测试代码如下所示
public class RetryTemplate08 { public static void main(String[] args) throws Exception { RetryTemplate template new RetryTemplate(); CircuitBreakerRetryPolicy retryPolicy new CircuitBreakerRetryPolicy(new SimpleRetryPolicy(3)); retryPolicy.setOpenTimeout(5000); retryPolicy.setResetTimeout(20000); template.setRetryPolicy(retryPolicy); for (int i 0; i 10; i) { try { Object key circuit; boolean isForceRefresh false; RetryState state new DefaultRetryState(key, isForceRefresh); String result template.execute(new RetryCallbackString, RuntimeException() { Override public String doWithRetry(RetryContext context) throws RuntimeException { System.out.println(retry count: context.getRetryCount()); throw new RuntimeException(timeout); } }, new RecoveryCallbackString() { Override public String recover(RetryContext context) throws Exception { return default; } }, state); System.out.println(result); } catch (Exception e) { System.out.println(e); } } }
} XML Configuration xml配置可以在不修改原来代码的情况下通过添加spring retry的功能。
SpringBootApplication EnableRetry EnableAspectJAutoProxy ImportResource(classpath:/retryadvice.xml) public class XmlApplication { public static void main(String[] args) { SpringApplication.run(XmlApplication.class, args); } } public class XmlRetryService { public void xmlRetryService(String arg01) throws Exception { System.out.println(xmlRetryService do something...); throw new RemoteAccessException(RemoteAccessException....); } }
?xml version1.0 encodingUTF-8? beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlnshttp://www.springframework.org/schema/beans xmlns:aophttp://www.springframework.org/schema/aop xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd aop:config aop:pointcut idtransactional expressionexecution(*XmlRetryService.xmlRetryService(..)) / aop:advisor pointcut-reftransactional advice-reftaskRetryAdvice order-1 / /aop:config bean idtaskRetryAdvice classorg.springframework.retry.interceptor.RetryOperationsInterceptor property nameRetryOperations reftaskRetryTemplate / /bean bean idtaskRetryTemplate classorg.springframework.retry.support.RetryTemplate property nameretryPolicy reftaskRetryPolicy / property namebackOffPolicy refexponentialBackOffPolicy / /bean bean idtaskRetryPolicy classorg.springframework.retry.policy.SimpleRetryPolicy constructor-arg index0 value5 / constructor-arg index1 map entry keyorg.springframework.remoting.RemoteAccessException valuetrue / /map /constructor-arg /bean bean idexponentialBackOffPolicy classorg.springframework.retry.backoff.ExponentialBackOffPolicy property nameinitialInterval value300 / property namemaxInterval value30000 / property namemultiplier value2.0 / /bean /beans