SpringCloud Alibaba Sentinel is the most popular fuse downgrade framework at present. The simple and easy-to-use way can quickly help us to achieve flow limiting and downgrade of services and ensure the stability of services.
What is Sentinel?
With the popularity of microservices, stability between services becomes increasingly important. Sentinel takes flow as the entry point to protect the stability of service from multiple dimensions such as flow control, fusing downgrading and system load protection.
There is no denying that Sentinel has rich features and provides configurations in a good dashboard. However, Sentinel needs to introduce multiple dependencies when integrating into the project, and it requires reading relevant documents and relevant configurations in the dashboard before being integrated into the project, which is a relatively complicated process.
If our project does not need so much functions, just need when an exception occurs when a method or a function can be downgraded, and is not directly the interrupt program, the business function is not the main process, then we in order to achieve such a small function, to integrate the Sentinel is clearly more complex to the project process, At this point, we need to implement a simple general way to degrade functionality. Let’s take a look at a simple Sentinel implementation
Of course, to do this, all we need is a try catch, but do we need a try catch? No! You don’t want to see a screen full of try-catch, and then delete line by line when this method doesn’t need to degrade.
Sample code is available at Github: github.com/chenliang15…
Custom annotation
The first step is to define a common annotation that helps us achieve non-invasive degradation and provides rich properties that make annotations more versatile and flexible
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Inherited
public @interface DegradeResource {
// The name of the degraded method
String fallback(a);
// Name of the degraded class, optionalClass<? >[] fallbackClass()default {};
// Specify degradation exception, optional
Class<? extends Throwable>[] exceptionHandle() default {};
}
Copy the code
-
Fallback: specifies the name of the fallback method. This parameter is mandatory. You must specify the name of the fallback method to invoke the fallback method when exceptions occur.
Notice of degradation method:
- Must be a public
- The method return type and method parameter must be the same as the original method, and the last parameter can be one more
Throwable
To receive exceptions that occur
-
FallbackClass: Specifies the class of the degraded method. This parameter is optional. If this parameter is not specified, the degraded method is in the current class by default
-
ExceptionHandle: Specifies the exception handling. This parameter is optional. ExceptionHandle is selected to degrade only when a specified exception occurs
Define the section processor
Once an asset degrades its annotation definition, we need a section processor to manage the degradation of the definition and the @degraderesource annotation that is added to the called method
@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);
// Check whether it is the same exception
if (exceptionBelongTo(e, exceptionList)) {
return handleFallbackMethod(pjp, degradeResource, e);
} else {
throwe; }}returnhandleFallbackMethod(pjp, degradeResource, e); }}/**
* if the throw exception is belong to exception trace list
*
* @param e
* @param exceptionList
* @return* /
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;
}
/** * 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 classClass<? > clazz = degradeResource.fallbackClass().length >0 ? degradeResource.fallbackClass()[0] : pjp.getTarget().getClass();
// Get the name of the currently executing method
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())) {
returnmethod; }}return null; }}Copy the code
The overall process is as follows: When scanning plane, the first step to normal execution method, method of when an exception occurs, judge whether the current setting is unusual, if you do not specify a type of exception handling, then the default degradation method, if the current type specifies the relegation of exception handling, then judging whether the exception thrown to the current method to deal with exception, if is a method that is called the drop If it is not an exception that needs to be handled, throw an exception.
Simplified exception degradation that fits the needs of the current scenario
Test the drop
A total of three methods of exception degradation are tested, which are the default all exception degradation, specified exception degradation, specified degradation method of the processing class
@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;
}
/** * support to specify the class of the fallback method, to place the degraded method in the specified 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 can take only the arguments of the original function */
public String findByIdFromCacheFallback1(String id) {
return "fallback1 = " + id;
}
/** * Fallback method can accept not only the arguments of the original method, but also the specific Exception ** /
public String findByIdFromCacheFallback2(String id, Throwable e) {
System.out.println("fallback method exception:" + e);
return "fallback2 = "+ id; }}Copy the code
Results:
It is possible to degrade the main flow when an exception occurs. For features that are not separated into a normal flow, we can use the @degraderesource comment to improve the flow and degrade it.
Did you Get it
Wechat public number fingertips on the code, welcome to pay attention to ~ learn together and progress together
Original is not easy, click a praise and then go ~ welcome to pay attention to, to bring you more wonderful articles!
Your likes and attention are the biggest motivation for writing articles