基于SpringCloud手写一个简易版Sentinel

目录

  • 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@Inheritedpublic @interface DegradeResource {// 降级的方法名称String fallback(); // 降级的类名称,可选Class[] fallbackClass() default {}; // 指定降级异常,可选Class[] exceptionHandle() default {}; }

  • fallback:降价方法的名称,需要指定降级方法的名称,才可以在发生异常时调用降级方法,必选参数。
降级方法须知:
必须为public
方法返回类型、方法参数必须和原始方法保持一致,最后一个参数允许多一个Throwable,用来接收发生的异常
  • fallbackClass:指定降级方法所在的class,可选参数,如果不指定则默认降级方法在当前class中
  • exceptionHandle:指定异常处理,当发生指定的异常时才选择进行降级,可选参数,数组类型,可以接收多个异常类型

定义切面处理器
当资源降级注解定义之后,我们就需要一个切面处理器,对定义的降级注解做切面处理,当调用的方法上有@DegradeResource注解时,会通过切面处理器进行处理
@Aspectpublic 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 listClass[] exceptions = degradeResource.exceptionHandle(); if(exceptions.length > 0) {List> exceptionList = Arrays.asList(exceptions); // 判断是否为同一个个异常if (exceptionBelongTo(e, exceptionList)) {return handleFallbackMethod(pjp, degradeResource, e); } else {throw e; }}return handleFallbackMethod(pjp, degradeResource, e); }}/*** if the throw exception is belong to exception trace list** @param e* @param exceptionList* @return*/private boolean exceptionBelongTo(Throwable e, List> exceptionList) {for (Class aClass : exceptionList) {if(aClass.isAssignableFrom(e.getClass())) {return true; }}return false; }/*** invoke fallback method**/private Object handleFallbackMethod(ProceedingJoinPoint pjp, DegradeResource degradeResource, Throwable e) throws Throwable {// fallback methodString fallback = degradeResource.fallback(); if(StringUtils.isEmpty(fallback)) {throw e; }// fallback classClass 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 argsObject[] args; Object[] originArgs = pjp.getArgs(); int paramCount = fallbackMethod.getParameterTypes().length; if(originArgs.length == paramCount) {args = originArgs; } else {// fill throwable to fallback method argsargs = Arrays.copyOf(originArgs, originArgs.length + 1); args[args.length - 1] = e; }// if staticif(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 paramsMethod method = findMethod(clazz, originParams, fallbackName, signers.getReturnType()); if(method == null) {// find fallback method with exception paramsmethod = 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中方式的异常降级,分别为默认所有异常降级、指定异常降级、指定降级方法的处理类
@Servicepublic 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; }/*** 支持指定fallback method的class,将降级方法统一放置指定的class中**/@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; }/*** fallback method可以只接受原始函数的参数*/public String findByIdFromCacheFallback1(String id) {return "fallback1 = " + id; }/*** fallback method 不仅可以接收原始方法的参数,还可以接收具体的Exception**/public String findByIdFromCacheFallback2(String id, Throwable e) {System.out.println("fallback method exception:" + e); return "fallback2 = " + id; }}

结果:
基于SpringCloud手写一个简易版Sentinel
文章图片

可以看到,当发生异常时,可以通过降级保证主流程的通常,对于非主流程的功能,我们可以通过@DegradeResource注解保证流程的完善和降级方案,保证用户的体验和程序的正常。
【基于SpringCloud手写一个简易版Sentinel】以上就是基于SpringCloud手写一个简易版Sentinel的详细内容,更多关于SpringCloud手写Sentinel的资料请关注脚本之家其它相关文章!

    推荐阅读