发布时间:2025-12-10 22:58:26 浏览次数:1
在项目开发中,有的时候,总是避免不了调用第三方服务接口,有的时候可能因为网络等情况的因素,我们需要重试几次才能调用成功,所以就需要一重试机制来保证接口的正常访问。
这是谷歌guava提供的工具包,我们可以在项目中引入相应的包,很轻松实现重试访问。guava-retrying中大量运用的策略模式,可以自定义各种执行重试策略。下面是简单实用步骤。
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>19.0</version></dependency><dependency><groupId>com.github.rholder</groupId><artifactId>guava-retrying</artifactId><version>2.0.0</version></dependency>
我这里实现一个对接口的简单访问,以此来模拟访问第三方接口。
@GetMapping("/retrytest")publicMap<String,Object>retrytest(){Map<String,Object>resultMap=Maps.newHashMap();resultMap.put("id",1001L);resultMap.put("msg","测试");Map<String,Object>tokenMap=Maps.newHashMap();tokenMap.put("token",UUID.randomUUID().toString());resultMap.put("data",tokenMap);returnresultMap;}Retryer中定义重试的各种策略,在执行call方法的时候,会将这些重试策略一一使用。
RetryListener是重试监听器,可以监听每次重试的过程。
BlockStrategy是自定义阻塞策略。
@BeanpublicRetryer<String>retry(){RetryListenerretryListener=newRetryListener(){@Overridepublic<V>voidonRetry(Attempt<V>attempt){try{if(attempt.hasException()){log.error("---"+Arrays.toString(attempt.getExceptionCause().getStackTrace()));}else{log.info("---"+attempt.get().toString());}}catch(Exceptione){e.printStackTrace();}}};BlockStrategyblockStrategy=newBlockStrategy(){@Overridepublicvoidblock(longsleepTime)throwsInterruptedException{LocalDateTimestartTime=LocalDateTime.now();longstart=System.currentTimeMillis();longend=start;log.info("[SpinBlockStrategy]...beginwait.");while(end-start<=sleepTime){end=System.currentTimeMillis();}//使用Java8新增的Duration计算时间间隔Durationduration=Duration.between(startTime,LocalDateTime.now());log.info("[SpinBlockStrategy]...endwait.duration={}",duration.toMillis());}};Retryer<String>retryer=RetryerBuilder.<String>newBuilder()//retryIf重试条件.retryIfException().retryIfRuntimeException().retryIfExceptionOfType(Exception.class).retryIfException(Predicates.equalTo(newException()))//等待策略:每次请求间隔1s//.withWaitStrategy(WaitStrategies.fixedWait(1,TimeUnit.SECONDS))//停止策略:尝试请求3次.withStopStrategy(StopStrategies.stopAfterAttempt(3))//时间限制:某次请求不得超过2s,类似:TimeLimitertimeLimiter=newSimpleTimeLimiter();.withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(2,TimeUnit.SECONDS)).withRetryListener(retryListener).withBlockStrategy(blockStrategy).build();returnretryer;}编写retryer调用call方法的具体实现逻辑。
@Slf4jpublicclassRetryCallableimplementsCallable<String>{privateRestTemplaterestTemplate;publicRetryCallable(RestTemplaterestTemplate){this.restTemplate=restTemplate;}inttimes=1;@OverridepublicStringcall(){log.info("calltimes={}",times++);log.info("请求时间:{}",LocalDateTime.now());//对远程地址的访问returnObjects.requireNonNull(restTemplate.getForObject("http://localhost:8080/retrytest",Object.class)).toString();}}@GetMapping("/hello")publicObjecthello(@RequestParam(required=false)Stringtoken){log.info("hello"+token);Mapresult=null;Stringmsg="";try{//定义请求实现利用重试器调用请求StringcallResult=retryer.call(newRetryCallable(restTemplate));result=newGson().fromJson(callResult,Map.class);}catch(Exceptione){e.printStackTrace();msg=e.getMessage();log.warn("请求失败:{}",e.getMessage());}HashMap<String,Object>resultData=Maps.newHashMap();resultData.put("data",result);resultData.put("token",token);resultData.put("msg",msg);returnresultData;}调用接口测试
2019-10-1213:46:23.863INFO68012---[nio-8080-exec-1]com.wj.retry.controller.HelloController:hellof5b78e95-87f7-435e-b9be-04bcb88ad0562019-10-1213:46:23.865INFO68012---[pool-1-thread-1]com.wj.retry.controller.RetryCallable:calltimes=12019-10-1213:46:23.875INFO68012---[pool-1-thread-1]com.wj.retry.controller.RetryCallable:请求时间:2019-10-12T13:46:23.8742019-10-1213:46:24.068INFO68012---[nio-8080-exec-1]com.wj.retry.RetryApplication:---{msg=测试,data={token=eacb4a99-9ef9-4581-b8e5-28fdabac1c52},id=1001}若失败会调用多次(按照重试器中定义的策略)并,抛出异常
上面的实现会写相对较多的代码,若使用spring-retry则相对简单多了,可以基于注解实现
<dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId></dependency>
@Retryable的参数说明:
value:抛出指定异常才会重试
include:和value一样,默认为空,当exclude也为空时,默认所以异常
exclude:指定不处理的异常
maxAttempts:最大重试次数,默认3次
backoff:重试等待策略,默认使用@Backoff,@Backoff的value默认为1000L,我们设置为2000L;multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。
@Retryable(value=Exception.class,maxAttempts=3,backoff=@Backoff(delay=2000L,multiplier=1.5))publicObjectretry(@RequestParam(required=false)Stringtoken){Stringmsg="";log.info("springretry");log.info("请求时间:{}",LocalDateTime.now());StringresultStr=Objects.requireNonNull(restTemplate.getForObject("http://localhost:8080/retrytest",Object.class)).toString();Mapresult=newGson().fromJson(resultStr,Map.class);HashMap<String,Object>resultData=Maps.newHashMap();resultData.put("data",result);resultData.put("token",token);resultData.put("msg",msg);returnresultData;}最后在启动类上加上@EnableRetry注解开启重试机制。ok,就这样两个步骤就完成了
除了上面调用第三方服务接口可能会用到重试机制,在微服务项目中,服务之间的通信,重试机制可以说是随处可见。
在springcloud中Ribbon,feign,Hystrix等组件都可以自己配置重试机制,来达到提高能正常通信的成功率。
此外,在各种消息中间件中也都有重试机制的体现,例如kafka,消息发送失败可以重新发送,消息消费失败了,也可以配置重试机制,可以最大程度达到消息的不丢失。
可以考虑这么一个事情,若目标逻辑执行时间过长,超出了重试的等待时间,客户端就要发起重试,那么服务端就会出现重复调用执行的问题,所以,有重试机制就要考虑幂等性的问题。
到此,关于“Retry重试机制是什么意思”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注本站网站,小编会继续努力为大家带来更多实用的文章!