电脑装配网

远程过程调用失败且未执行怎么回事(远程过程调用失败且未执行u盘)

 人阅读 | 作者xiaolin | 时间:2022-09-20 19:34

在日常开发的过程中我们经常会需要调用第三方组件或者数据库,有的时候可能会因为网络抖动或者下游服务抖动,导致我们某次查询失败。

这种时候我们往往就会进行重试,当重试几次后依旧还是失败的话才会向上抛出异常进行失败。接下来阿粉就给大家演示一下通常是如何做的,以及如何更优雅的进行重试。

常规做法

我们先来看一下常规做法,常规做法首先会设置一个重试次数,然后通过 while 循环的方式进行遍历,当循环次数没有达到重试次数的时候,直到有正确结果后就返回,如果重试依旧失败则会进行睡眠一段时间,再次重试,直到正常返回或者达到重试次数返回。

package com.example.demo.service;import org.Springframework.retry.annotation.Backoff;import org.springframework.retry.annotation.Retryable;import org.springframework.stereotype.Service;import java.util.Random;import java.util.concurrent.TimeUnit;@Servicepublic class HelloService { public String sayHello(String name) { String result = ""; int retryTime = 3; while (retryTime > 0) { try { // result = name doSomething(); return result; } catch (Exception e) { System.out.println("send message failed. try again in 1's"); retryTime--; try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException ex) { throw new RuntimeException(ex); } } } return result; } private int doSomething() { RequestParam;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HelloController { @Autowired private HelloService helloService; @GetMapping(value = "/hello") public String hello(@RequestParam("name") String name) { return helloService.sayHello(name); }}

正常启动过后,我们通过浏览器进行访问。

可以看到,我们第一次方法的时候就成功的达到了我们要的效果,随机数就是 0 ,在 1 秒后重试后结果正常。在多试了几次过后,会遇到三次都是 0 的情况,这个时候才会抛出异常,说明服务是真的有问题了。

上面的代码可以看到是有效果了,虽然不是很好看,特别是在还有一些其他逻辑的情况,看上去会很臃肿,但是确实是可以正常使用的,那么有的小伙伴就要问了,有没有一种优雅的方式呢?总不能在很多地方都重复的这样写重试的代码吧。

注解重试

要知道我们普通人在日常开发的时候,如果遇到一个问题肯定是别人都遇到过的,什么时候当我们遇到的问题,没有人遇到过的时候,那说明我们是很前卫的。

因此小伙伴能想到的是不是有简单的方式来进行重试,有的人已经帮我们想好了,可以通过 @Retryable 注解来实现一样的效果,接下来阿粉就给大家演示一下如何使用这个extends Throwable>[] value() default {}; Class<? extends Throwable>[] include() default {}; Class<? extends Throwable>[] exclude() default {}; String label() default ""; boolean stateful() default false; int maxAttempts() default 3; String maxAttemptsExpression() default ""; Backoff backoff() default @Backoff; String exceptionExpression() default ""; String[] listeners() default {};}

点到这个注解里面,我们可以看到这个注解的代码如下,其中有几个参数我们来解释一下

  • recover: 当前类中的回滚方法名称;
  • interceptor: 重试的拦截器名称,重试的时候可以配置一个拦截器;
  • value:需要重试的异常类型,跟下面的 include 一致;
  • include:包含的重试的异常类型;
  • exclude:不包含的重试异常类型;
  • label:用于统计的唯一标识;
  • stateful​:标志表示重试是有状态的,也就是说,异常被重新抛出,重试策略是否会以相同的策略应用于具有相同参数的后续调用。如果是false,那么可重试的异常就不会被重新抛出。
  • maxAttempts:重试次数;
  • backoff:指定用于重试此操作的属性;
  • listeners​:重试监听器bean 名称;

配合上面的一些属性的使用,我们就可以达到通过注解简单来实现方法调用异常后的自动重试,非常好用。我们可以在执行重试方法的时候设置自定义的重试拦截器,如下所示,自定义重试拦截器需要实现 MethodInterceptor​ 接口并实现 invoke 方法,不过要注意,如果使用了拦截器的话,那么方法上的参数就会被覆盖。

package com.example.demo.pid;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;import org.springframework.retry.interceptor.RetryInterceptorBuilder;import org.springframework.retry.interceptor.RetryOperationsInterceptor;import org.springframework.retry.policy.SimpleRetryPolicy;import org.springframework.stereotype.Component;@Componentpublic class CustomRetryInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { RetryOperationsInterceptor build = RetryInterceptorBuilder.stateless() .maxAttempts(2).backOffOptions(3000, 2, 1000).build(); return build.invoke(invocation); }}

自定义回滚方法,我们还可以在重试几次依旧错误的情况,编写自定义的回滚方法。

@Retryable(value = Exception.class, recover = "recover", maxAttempts = 2, backoff = @Backoff(delay = 1000, multiplier = 2)) public String sayHello(String name){ return name doSomething(); } @Recover public String recover(Exception e, String name) { System.out.println("recover"); return "recover"; }

要注意:

  • 重试方法必须要使用@Recover 注解;
  • 返回值必须和被重试的函数返回值一致;
  • 参数中除了第一个是触发的异常外,后面的参数需要和被重试函数的参数列表一致;

上面代码中的 @Backoff(delay = 1000, multiplier = 2) 表示第一次延迟 1000ms 重试,后面每次重试的延迟时间都翻倍。

总结

阿粉今天给大家介绍了一下 Spring​ 的 @Retryable 注解使用,并通过几个 demo 来带大家编写了自己重试拦截器以及回滚方法的时候,是不是感觉用起来会很爽,那还在等什么赶紧用起来吧,其中还有很多细节,只有自己真正的使用过才能体会到。


文章标签:

本文链接:『转载请注明出处』