• java业务代码自动降级实现


    业务需求

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

    需要实现的功能

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

    比较传统的做法是使用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 "";
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    使用方式类似如下:

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

    切面和切点配置

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

    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();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

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

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

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

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

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

    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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    使用方法

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

    实现自动降级

    在某些情况下,我们要实现在原始方法发生异常的时候,自动进行降级处理,比如接口调用超时等异常,一种方法是,在每一个方法里都进行手动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());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

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

    作者:tengqingya
    转载请保留出处

  • 相关阅读:
    54、Neural 3D Video Synthesis from Multi-view Video
    并查集(UnionFind)总结
    【C++入门系列】——命名空间和输入输出
    聚苯乙烯-甲基丙烯酸-3-三甲氧基硅丙酯离子交换树脂微球/二氧化硅@聚苯乙烯@聚吡咯三层复合物微球性能
    Java 常用API
    PyTorch中特殊函数梯度的计算
    了解WebGL三维技术
    [需求管理-7]:需求分析 - 如何进行有商业价值需求的技术影响分析?
    100天精通Python(基础篇)——第8天:字符串的三种定义
    运维经验记录 在CentOS上挂载Windows共享磁盘
  • 原文地址:https://blog.csdn.net/starryninglong/article/details/126070865