java业务代码自动降级实现

8 篇文章 0 订阅
3 篇文章 0 订阅

业务需求

因业务需要,需要对调用其他部门的接口,做自动降级逻辑的实现,也就是说,在其他部门的接口发生异常的时候,需要进行降级处理,并且降级逻辑需要可配置可控,并且可以按照部门维度进行降级。

需要实现的功能

手动触发开关进行开启和关闭降级

比较传统的做法是使用Hystrix,步骤如下

  • 项目中引入 Hystrix 依赖
  • 项目启动类添加 @EnableHystrix 修饰开启 Hystrix
  • 设置指定需要降级处理的方法,提供降级方法
  • 在配置中心增加配置进行降级的开关

但是Hystrix的功能比较庞大,例如隔离、熔断、降级机制等等,本需求只需要降级和按部门维度降级。故决定自己实现一个简易版的降级功能。

降级开关没开启的时候,如果可降级的方法异常则自动降级

一般的,降级其实就是一个兜底的过程,但是往往异常是不可预知的,所以,如果可以降级的方法发生了异常,需要自动降级,这一点Hystrix的功能完全能够满足。但是Hystrix学习成本比较高,我们要做的就是发生异常就降级,很简单就能实现。

具体实现方式

自定义降级注解

我们设想的是,在需要降级的方法上,加上一个注解,就能够实现降级的全部功能。所以先定义一个注解。

package com.f4.ts.enterprise.degrade;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 是否需要降级的注解配置
 *
 * @author tengqy
 * @create 2022-04-21 8:35
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnterpriseDegrade {
    /**
     * 是否降级
     *
     * @return
     */
    boolean value() default true;

    /**
     * 需要降级的部门接口
     * 假设有部门A和部门B
     * 部门A有接口C和D
     * 部门B有接口E和F
     * 则在C和D的方法上配置A,并且降级开关打开的时候,就会直接降级接口C和D
     * @return
     */
    String[] bu() default "";

    /**
     * 降级日志描述,方便定位问题
     *
     * @return
     */
    String desc() default "";
}

使用方式类似如下:

	@EnterpriseDegrade(bu = {A}, desc = "不进行接口C的调用")
    public Response C() {}

切面和切点配置

定义好了注解,则需要定义注解的处理方法,也就是切面和切点的配置,以及是进行何种处理,是前置处理还是后置处理还是环绕处理。
代码如下:

package com.f4.ts.enterprise.degrade;

import cn.hutool.core.util.ReflectUtil;
import com.f4.ts.enterprise.config.DegradeConfig;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 降级切面实现
 *
 * @author tengqy
 * @create 2022-04-21 8:37
 */
@Aspect
@Slf4j
@Component
@SuppressWarnings("all")
public class EnterpriseDegradeAspect {
    @Autowired
    private DegradeConfig degradeConfig;

    public EnterpriseDegradeAspect() {
    }

    @Pointcut("@annotation(com.f4.ts.enterprise.degrade.EnterpriseDegrade)")
    public void annotationPointcut() {
    }


    @Around("annotationPointcut() && @annotation(EnterpriseDegrade)")
    public Object doAround1(ProceedingJoinPoint joinPoint) throws Throwable {
        if (degradeConfig.getStartDegrade()) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            String name = method.getName();
            EnterpriseDegrade annotation = method.getAnnotation(EnterpriseDegrade.class);
            String[] bu = annotation.bu();
            if (Boolean.TRUE.equals(annotation.value())) {
                if (bu != null && bu.length > 0) {
                    for (String s : bu) {
                        if (degradeConfig.getBus().contains(s)) {
                            //指定的部门接口需要降级
                            
                        }
                    }
                }
            }
        }
        return joinPoint.proceed();
    }
}

以上代码,就是在配置了EnterpriseDegrade的方法上,进行环绕处理,其中degradeConfig使用的Apollo的配置,bus为需要进行降级的部门集合,startDegrade为是否开启降级

	@Value("#{'${degrade.bus}'.split(',')}")
    private List<String> bus;
    @Value("${degrade.startDegrade:false}")
    private Boolean startDegrade;

配置降级后需要调用的方法

在使用Hystrix的时候我们经常会这样配置降级方法

@HystrixCommand(fallbackMethod = "indexError")
public Object index() {
        return restTemplate.getForObject("http://testservice", String.class);
    }
public Object indexError() {
        return "{\"code\": 999,\"message\": \"服务断路\"}";
    }

index方法发生异常并降级后,会自动调用indexError的方法,进行降级处理。
所以我们也这样使用,但是我们固定一下,如果方法A调用失败后,自动调用A的降级方法ADegrade,固定为原方法名称+“Degrade”,这样我们就省略了fallbackMethod的配置
则原降级切面方法配置代码如下:

package com.f4.ts.enterprise.degrade;

import cn.hutool.core.util.ReflectUtil;
import com.f4.ts.enterprise.config.DegradeConfig;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 企业降低切面
 * 降级切面实现
 *
 * @author tengqy
 * @create 2022-04-21 8:37
 * @date 2022/04/22
 */
@Aspect
@Slf4j
@Component
public class EnterpriseDegradeAspect {
    /**
     * 降级配置
     */
    @Autowired
    private DegradeConfig degradeConfig;

    
    public EnterpriseDegradeAspect() {
    }

    /**
     * 切入点
     */
    @Pointcut("@annotation(com.f4.ts.enterprise.degrade.EnterpriseDegrade)")
    public void annotationPointcut() {
    }


    /**
     * 
     *
     * @param joinPoint 连接点
     * @return {@link Object}
     * @throws Throwable throwable
     */
    @Around("annotationPointcut() && @annotation(EnterpriseDegrade)")
    public Object doAround1(ProceedingJoinPoint joinPoint) throws Throwable {
        if (degradeConfig.getStartDegrade()) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            String name = method.getName();
            EnterpriseDegrade annotation = method.getAnnotation(EnterpriseDegrade.class);
            String[] bu = annotation.bu();
            String desc = annotation.desc();
            if (Boolean.TRUE.equals(annotation.value())) {
                if (bu != null && bu.length > 0) {
                    for (String s : bu) {
                        if (degradeConfig.getBus().contains(s)) {
                            //指定的bu需要降级
                            Method actualMethod = ReflectUtil.getMethodByName(joinPoint.getTarget().getClass(), name + "Degrade");
                            actualMethod.setAccessible(Boolean.TRUE);
                            log.info("开始降级,方法为:{},参数为:{},bu为:{},具体描述为:{}", method.getName(), joinPoint.getArgs(), s, desc);
                            return actualMethod.invoke(joinPoint.getTarget(), joinPoint.getArgs());
                        }
                    }
                }
            }
        }
        //开关关闭,不降级则调用原始方法
        return joinPoint.proceed();
    }
}

使用方法

	//原始业务方法
	@EnterpriseDegrade(bu = {A}, desc = "不进行接口C的调用")
    public Response C() {}
    //降级后调用的方法
    public Response CDegrade() {}

实现自动降级

在某些情况下,我们要实现在原始方法发生异常的时候,自动进行降级处理,比如接口调用超时等异常,一种方法是,在每一个方法里都进行手动try catch,然后手动调用降级后的方法,这样通俗易懂,但是需要在每一个方法里面都修改,不优雅,既然我们已经使用的注解和切面切点来进行降级,那么为何不进一步加个异常处理呢?具体实现方法代码如下

package com.f4.ts.enterprise.degrade;

import cn.hutool.core.util.ReflectUtil;
import com.alibaba.dubbo.rpc.RpcException;
import com.f4.ts.enterprise.config.DegradeConfig;
import com.f4.ts.utils.StringUtils;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.client.ResourceAccessException;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 
 * 降级切面实现
 *
 * @author tengqy
 * @create 2022-04-21 8:37
 * @date 2022/04/22
 */
@Aspect
@Slf4j
@Component
public class EnterpriseDegradeAspect {
    /**
     * 降级配置
     */
    @Autowired
    private DegradeConfig degradeConfig;


    public EnterpriseDegradeAspect() {
    }


    @Pointcut("@annotation(com.f4.ts.enterprise.degrade.EnterpriseDegrade)")
    public void annotationPointcut() {
    }


    /**
     *
     * @param joinPoint 连接点
     * @return {@link Object}
     * @throws Throwable throwable
     */
    @Around("annotationPointcut() && @annotation(EnterpriseDegrade)")
    public Object doAround1(ProceedingJoinPoint joinPoint) throws Throwable {
        if (degradeConfig.getStartDegrade()) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            String name = method.getName();
            EnterpriseDegrade annotation = method.getAnnotation(EnterpriseDegrade.class);
            String[] bu = annotation.bu();
            String desc = annotation.desc();
            if (Boolean.TRUE.equals(annotation.value())) {
                if (bu != null && bu.length > 0) {
                    for (String s : bu) {
                        if (degradeConfig.getBus().contains(s)) {
                            //指定的bu需要降级
                            Method actualMethod = ReflectUtil.getMethodByName(joinPoint.getTarget().getClass(), name + "Degrade");
                            actualMethod.setAccessible(Boolean.TRUE);
                            log.info("开始降级,方法为:{},参数为:{},bu为:{},具体描述为:{}", method.getName(), joinPoint.getArgs(), s, desc);
                            return actualMethod.invoke(joinPoint.getTarget(), joinPoint.getArgs());
                        }
                    }
                }
            }
        }
        try {
            return joinPoint.proceed();
        } catch (ResourceAccessException e) {
            return doInvokeAfterException(joinPoint, e);
        }
    }

    /**
     * 可降级的方法调用异常,自动降级
     * @param joinPoint join
     * @param e         e
     * @return Object
     * @throws IllegalAccessException i
     * @throws InvocationTargetException i
     */
    private Object doInvokeAfterException(ProceedingJoinPoint joinPoint, Exception e) throws IllegalAccessException, InvocationTargetException {
        log.error("可降级的方法调用异常,自动降级开启 ", e);
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String name = method.getName();
        EnterpriseDegrade annotation = method.getAnnotation(EnterpriseDegrade.class);
        String desc = annotation.desc();
        Method actualMethod = ReflectUtil.getMethodByName(joinPoint.getTarget().getClass(), name + "Degrade");
        actualMethod.setAccessible(Boolean.TRUE);
        log.info("开始自动降级,方法为:{},参数为:{},具体描述为:{}", method.getName(), joinPoint.getArgs(), desc);
        return actualMethod.invoke(joinPoint.getTarget(), joinPoint.getArgs());
    }
}

至此,我们就实现了一个能自动降级、能开关控制降级、能按照部门维度降级,只需要加注解和写一个降级方法的全局降级工具了。

作者:tengqingya
转载请保留出处

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值