我的编程空间,编程开发者的网络收藏夹
学习永远不晚

基于SpringCloud手写一个简易版Sentinel

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

基于SpringCloud手写一个简易版Sentinel

Sentinel 是什么?

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

不可否认的是,Sentinel功能丰富,并且在提供好用的dashboard提供配置,但是Sentinel在集成到项目中时需要引入多个依赖,并且需要阅读相关文档,以及dashboard中的相关配置才可以接入到项目中,这个过程还是较为复杂的。

如果我们的项目并不需要这么多的功能,只是需要当某个方法或者某个功能发生异常的时候可以实现降级,并不是直接中断程序,该业务功能不是主流程,那么我们为了实现这样一个小功能的时候,将Sentinel集成到项目中的过程显然是较为复杂的,那么这个时候,就需要我们实现一个简答的功能降级的通用方式,下面就一起看看一个简易版的Sentinel的实现

当然,实现这个功能,只需要一个try-catch就可以搞定个,但是我们需要的是try-catch吗?No! 我们需要的是优雅~ 我想你也不想看到满屏的try-catch吧,如果哪天这个方法无需降级的时候,再去一行一行删代码吗?

示例代码已收录到Github: github.com/chenliang15…

定义注解

第一步,定义一个通用注解,这个注解可以帮助我们无侵入性的实现功能降级,并且提供丰富的属性,让注解的通用性和灵活性更强


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Inherited
public @interface DegradeResource {

    // 降级的方法名称
    String fallback();
	
    // 降级的类名称,可选
    Class<?>[] fallbackClass() default {};

    // 指定降级异常,可选
    Class<? extends Throwable>[] exceptionHandle() default {};

}
  • fallback:降价方法的名称,需要指定降级方法的名称,才可以在发生异常时调用降级方法,必选参数。

降级方法须知:

必须为public
方法返回类型、方法参数必须和原始方法保持一致,最后一个参数允许多一个Throwable,用来接收发生的异常

  • fallbackClass:指定降级方法所在的class,可选参数,如果不指定则默认降级方法在当前class中
  • exceptionHandle:指定异常处理,当发生指定的异常时才选择进行降级,可选参数,数组类型,可以接收多个异常类型

定义切面处理器

当资源降级注解定义之后,我们就需要一个切面处理器,对定义的降级注解做切面处理,当调用的方法上有@DegradeResource注解时,会通过切面处理器进行处理


@Aspect
public class DegradeResourceAspect {


    @Around("@annotation(degradeResource)")
    public Object doAround(ProceedingJoinPoint pjp, DegradeResource degradeResource) throws Throwable {
        try {
            return pjp.proceed();
        } catch(Throwable e){
            // need to trace exception list
            Class<? extends Throwable>[] exceptions = degradeResource.exceptionHandle();
            if(exceptions.length > 0) {
                List<Class<? extends Throwable>> exceptionList = Arrays.asList(exceptions);
                // 判断是否为同一个个异常
                if (exceptionBelongTo(e, exceptionList)) {
                    return handleFallbackMethod(pjp, degradeResource, e);
                } else {
                    throw e;
                }
            }
            return handleFallbackMethod(pjp, degradeResource, e);
        }
    }

    
    private boolean exceptionBelongTo(Throwable e, List<Class<? extends Throwable>> exceptionList) {
        for (Class<? extends Throwable> aClass : exceptionList) {
            if(aClass.isAssignableFrom(e.getClass())) {
                return true;
            }
        }
        return false;
    }

    
    private Object handleFallbackMethod(ProceedingJoinPoint pjp, DegradeResource degradeResource, Throwable e) throws Throwable {
        // fallback method
        String fallback = degradeResource.fallback();
        if(StringUtils.isEmpty(fallback)) {
            throw e;
        }
        // fallback class
        Class<?> clazz = degradeResource.fallbackClass().length > 0 ? degradeResource.fallbackClass()[0] : pjp.getTarget().getClass();

        // 获取当前执行的方法名称
        Method fallbackMethod = findFallbackMethod(pjp, clazz, fallback);
        if(Objects.isNull(fallbackMethod)) {
            throw e;
        }

        // fallback method args
        Object[] args;
        Object[] originArgs = pjp.getArgs();
        int paramCount = fallbackMethod.getParameterTypes().length;
        if(originArgs.length == paramCount) {
            args = originArgs;
        } else {
            // fill throwable to fallback method args
            args = Arrays.copyOf(originArgs, originArgs.length + 1);
            args[args.length - 1] = e;
        }

        // if static
        if(Modifier.isStatic(fallbackMethod.getModifiers())) {
            return fallbackMethod.invoke(null, args);
        }
        return fallbackMethod.invoke(clazz.newInstance(), args);
    }

    private Method findFallbackMethod(ProceedingJoinPoint pjp, Class<?> clazz, String fallbackName) {
        MethodSignature signers = (MethodSignature) pjp.getSignature();
        Class<?>[] originParams = signers.getParameterTypes();
        Class<?>[] paramsWithException = Arrays.copyOf(originParams, originParams.length + 1);
        paramsWithException[paramsWithException.length - 1] = Throwable.class;
        // find fallback method with origin params
        Method method = findMethod(clazz, originParams, fallbackName, signers.getReturnType());
        if(method == null) {
            // find fallback method with exception params
            method = findMethod(clazz, paramsWithException, fallbackName, signers.getReturnType());
        }
        return method;
    }

    private Method findMethod(Class<?> clazz, Class<?>[] paramsType, String fallbackName, Class<?> returnType) {
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            if(method.getName().equals(fallbackName)
                && returnType.isAssignableFrom(method.getReturnType())
                && Arrays.equals(paramsType, method.getParameterTypes())) {
                return method;
            }
        }
        return null;
    }

}

总体的流程为:当扫描到切面时,第一步先正常执行方法,当方法发生异常时,判断当前是否制定异常,如果没有指定异常处理类型,那么就默认走降级方法,如果当前指定了降级的异常处理类型,那么就判断当前方法抛出的异常是否为需要处理的异常,如果是则调用降级方法,如果不是需要处理的异常,那么就抛出异常。

符合当前场景的需要,简单化的异常降级

测试降级

总共测试了3中方式的异常降级,分别为默认所有异常降级、指定异常降级、指定降级方法的处理类


@Service
public class DegradeResourceTestService {


    @DegradeResource(fallback = "findByIdFromCacheFallback1")
    public String findById(String id) {
        int i = Integer.parseInt(id);
        System.out.println("id=" + id);
        return "ok = " + id;
    }

    @DegradeResource(fallback = "findByIdFromCacheFallback2", exceptionHandle = {NumberFormatException.class})
    public String findByIdWithException(String id) {
        int i = Integer.parseInt(id);
        System.out.println("id=" + id);
        return "ok = " + id;
    }

    
    @DegradeResource(fallback = "findByIdFromCacheFallback3", exceptionHandle = {NumberFormatException.class},
            fallbackClass = {FallbackClassService.class})
    public String findByIdWithFallbackClass(String id) {
        int i = Integer.parseInt(id);
        System.out.println("id=" + id);
        return "ok = " + id;
    }


    
    public String findByIdFromCacheFallback1(String id) {
        return "fallback1 = " + id;
    }

    
    public String findByIdFromCacheFallback2(String id, Throwable e) {
        System.out.println("fallback method exception:" + e);
        return "fallback2 = " + id;
    }

}

结果:

可以看到,当发生异常时,可以通过降级保证主流程的通常,对于非主流程的功能,我们可以通过@DegradeResource注解保证流程的完善和降级方案,保证用户的体验和程序的正常。

以上就是基于SpringCloud手写一个简易版Sentinel的详细内容,更多关于SpringCloud手写Sentinel的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

基于SpringCloud手写一个简易版Sentinel

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

基于python的Tkinter实现一个简易计算器

本文实例介绍了基于python的Tkinter实现简易计算器的详细代码,分享给大家供大家参考,具体内容如下 第一种:使用python 的 Tkinter实现一个简易计算器#coding:utf-8 from Tkinter import *
2022-06-04

基于Java制作一个简易的远控终端

这篇文章主要为大家详细介绍了如何基于Java制作一个简易的远控终端,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的可以了解一下
2023-05-14

如何使用Python写一个简易版弹球游戏

这篇文章主要介绍如何使用Python写一个简易版弹球游戏,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!先看一下我们的最终效果图我们分9步来讲解如何写这个小游戏1.创建游戏的主界面我们用Python的内置模块Tkint
2023-06-14

基于Python+Tkinter怎么实现一个简易计算器

这篇文章主要介绍“基于Python+Tkinter怎么实现一个简易计算器”,在日常操作中,相信很多人在基于Python+Tkinter怎么实现一个简易计算器问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”基于P
2023-06-26

基于Vue3编写一个简单的播放器

这篇文章主要为大家详细介绍了如何基于Vue3编写一个简单的播放器,文中的示例代码讲解详细,对我们学习Vue3有一定的帮助,需要的可以参考一下
2023-03-02

基于C++编写一个简单的服务器

这篇文章主要为大家详细介绍了如何基于C++编写一个简单的服务器,文中的示例代码讲解详细,具有一定的参考价值,感兴趣的小伙伴可以了解一下
2023-03-14

利用Java手写一个简易的lombok的示例代码

Lombok是一款Java开发插件,使得Java开发者可以通过其定义的一系列注解来消除业务工程中冗长和繁琐的代码,尤其对于简单的Java模型对象。本文就来手写一个简易的lombok,需要的可以参考一下
2022-11-13

基于Python编写一个简单的http服务器

这篇文章主要为大家详细介绍了如何基于Python编写一个简单的http服务器,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下
2023-05-17

基于Java编写一个简单的风控组件

这篇文章主要为大家详细介绍了如何基于Java编写一个简单的风控组件,文中的示例代码讲解详细,对我们学习Java有一定的帮助,需要的可以参考一下
2022-12-31

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录