基于SpringCloud手写一个简易版Sentinel

网友投稿 223 2023-01-14

基于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> 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 extends Throwable> 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 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 fallbackMeXRsgonthod = 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 findFallbXRsgonackMethod(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;

}

/**

* 支持指定fallback method的class,将降级方法统一放置指定的class中

*

*/

@DegradeResource(fallback = "findByIdFromCacheFallback3", exceptionHandle = {NumberFormatException.class},

fallbackClass =XRsgon {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;

}

}

结果:

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

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

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:IDEA 如何控制编辑左侧的功能图标ICON(操作步骤)
下一篇:网易云音乐免费api(网易云音乐免费下载app)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~