16、实现Client远程调用的重试机制

发布时间 2023-10-20 11:29:12作者: 爱文(Iven)

由于远程程序服务健壮性和网络的波动等因素,可能造成接口调用失败,因此有必要实现Client远程调用的重试机制

一、基于异常捕捉的重试机制:

    public String getDetailFromClient(){
        //重试次数
        int retryCount = 3;
        //重试时长(单位:ms)
        int retryTimes = 5000;
        while (retryCount > 0) {
            log.info("重试次数{}", retryCount);
            try {
                //远程调用的服务
                String result = httpClient.list();
             //判断返回的数据是否为零
                if (ObjectUtils.isEmpty(result)) {
                    log.info("数据为空,开始重试,retryTimes:{}", retryTimes);
                    //返回数据为零,进行重试
                    retryCount--;
                    //重试时长间隔
                    Thread.sleep(retryTimes);
                    continue;
                }
                // 返回非零数据,直接返回结果
                return result;
            } catch (Exception e) {
                //超时异常:进行重试
                retryCount--;
                try {
                    //重试时长间隔
                    Thread.sleep(retryTimes);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                log.error("调用发生异常,开始重试,retryTimes:{},异常信息{}", retryTimes, e);
            }
        }
        // 重试次数用尽,返回空值
        return "重试次数用尽,返回空值";
    }

 

二、基于Spring-AOP切面的重试机制:

1、引入AOP依赖:

<!--AOP切面-->
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-aop</artifactId>
     <version>2.6.13</version>
</dependency>    

2、自定义重试注解:

import java.lang.annotation.*;

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatableAnnotateBus {
 
    /**
     * 重试次数
     * @return
     */
    int repeatAmount() default 3;
 
    /**
     * 重试时间
     * @return
     */
    int repeatTime() default 1;
}

3、重试机制切面:

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

@Slf4j
@Component
@Aspect
public class RepeatableAspectUtil {
 
    @Pointcut("@annotation(com.iven.aspect.RepeatableAnnotateBus)")
    public void cutRepeatableMethod() {
 
    }
 
    @Around("cutRepeatableMethod()")
    public Object repeat(ProceedingJoinPoint joinPoint) throws InterruptedException {
        // 获取重试次数和重试间隔
        RepeatableAnnotateBus repeatableBus = ((MethodSignature) joinPoint.getSignature())
                .getMethod().getAnnotation(RepeatableAnnotateBus.class);
        int amount = repeatableBus.repeatAmount();
        int time = repeatableBus.repeatTime();
 
        Throwable error = new RuntimeException();
        for (int i = 1 ; i <= amount ; i++){
            try {
                //joinpoint.proceed():用于执行目标对象的原本方法,也就是切入点 (join point) 所表示的方法。
                //通过使用 proceed() 方法,可以在切面中实现方法拦截、日志记录等功能。
                Object result = joinPoint.proceed();
                if (ObjectUtils.isEmpty(result)) {
                    log.info("数据为空,开始重试,retryTimes:{}", i * time * 10000L);
                    //返回数据为零,进行重试, 重试时长间隔
                    Thread.sleep(i * time * 10000L);
                    continue;
                }
                return result;
            } catch (Throwable throwable) {
                error = throwable;
                log.warn("调用发生异常,开始重试,retryTimes:{}", i * time * 10000L);
            }
            //重试时长间隔
            Thread.sleep(i * time * 10000L);
        }
        log.error("重试次数耗尽,{}", error);
        return null;
    }
}

4、相关使用:

@RepeatableAnnotateBus
public String getDetailFromAspect(){
    //远程调用服务
    return httpClient.list();
}

 

三、参考:

java实现重试机制