Coding skills – Sentinel’s blockHandler and fallback

This article introduces the difference between Sentinel’s blockHandler and fallback. The background is: when current limiting occurs, the configured sentinel’s blockhandler does not take effect and fallback takes effect; check the cause, and then give the code writing method for Sentinel configuration abnormal downgrade and current limit downgrade;

Before viewing the source code, I consulted relevant technical posts (1. The difference between Sentinel’s blockHandler and fallback 2. Sentinel service fuse [fallBack/blockHandler]), for the scenario where fallback and blockHandler are configured at the same time, the conclusions are inconsistent, so I decided to practice by yourself;

1. Fallback and blockHandler are not configured

The code is as follows, only configure the value of SentinelResource, and go to the sentinel console to configure the single-machine current limit to 1;

Current limiting method:

 /**
     * Test sentinel's downgrade method
     */
    @SentinelResource(value = "testSentinelFallback")
    public String testSentinelFallback() {
        return "return success ok";
    }

Test code:

 @Override
    public String mock() {
        // Asynchronous call to simulate concurrency
        for (int i = 0; i < 5; i ++ ) {
            CompletableFuture. runAsync(() -> {
                try {
                    final String result = testSentinelService.testSentinelFallback();
                    log.info("The call returns the result [result={}]", result);
                } catch (Throwable e) {
                    log.warn("The call throws an exception", e);
                }
            });
        }
        return null;
    }

Sentinel background configuration:

Results of the:

call return result [result=return success ok]

call throws an exception
java.lang.reflect.UndeclaredThrowableException: null
Caused by: com.alibaba.csp.sentinel.slots.block.flow.FlowException: null

call throws an exception
java.lang.reflect.UndeclaredThrowableException: null
Caused by: com.alibaba.csp.sentinel.slots.block.flow.FlowException: null
...

2. Only configure blockHandler

2.1 No extra parameter BlockException when configuring blockHandler
 @SentinelResource(value = "testSentinelFallback", blockHandler = "myBlockHandler")
    public String testSentinelFallback() {
        return "return success ok";
    }

    public String myBlockHandler() {
        return "Enter myBlockHandler logic";
    }

Execution result: Did not enter current limit downgrade method;

call return result [result=return success ok]

call throws an exception
java.lang.reflect.UndeclaredThrowableException: null
Caused by: com.alibaba.csp.sentinel.slots.block.flow.FlowException: null

call throws an exception
java.lang.reflect.UndeclaredThrowableException: null
Caused by: com.alibaba.csp.sentinel.slots.block.flow.FlowException: null
...
2.2 Correctly configure blockHandler
 @SentinelResource(value = "testSentinelFallback", blockHandler = "myBlockHandler")
    public String testSentinelFallback() {
        return "return success ok";
    }

    public String myBlockHandler(BlockException blockException) {
        return "Enter myBlockHandler logic";
    }

Execution result: Successfully entered the current limiting and downgrading method;

call return result [result=return success ok]

Call return result [result=enter myBlockHandler logic]

Call return result [result=enter myBlockHandler logic]
...
2.3 blockHandler can capture business exceptions
 @SentinelResource(value = "testSentinelFallback", blockHandler = "myBlockHandler")
    public String testSentinelFallback() {
        if (Boolean. TRUE) {
            throw new BusinessException(FacadeResultCodeEnum.BAD_PARAMS);
        }
        return "return success ok";
    }

    public String myBlockHandler(BlockException blockException) {
        return "Enter myBlockHandler logic";
    }

Execution result: When the interface is limited, it successfully enters the current limiting downgrade method; when the interface has a business exception, it will be thrown to the outer layer;

The call throws an exception
BusinessException

Call return result [result=enter myBlockHandler logic]

Call return result [result=enter myBlockHandler logic]
...

3. Only configure fallback

3.1 The additional parameter Throwable is not included when configuring fallback
 @SentinelResource(value = "testSentinelFallback", fallback = "myFallback")
    public String testSentinelFallback() {
        if (Boolean. TRUE) {
            throw new BusinessException(FacadeResultCodeEnum.BAD_PARAMS);
        }
        return "return success ok";
    }

    public String myFallback() {
        return "Enter myFallback logic";
    }

Execution result: When the interface is limited, or when the interface has a business exception, it will enter the fallback downgrade method;

call return result [result=enter myFallback logic]

Call return result [result=enter myFallback logic]

Call return result [result=enter myFallback logic]
...

3.2 Bring additional parameter Throwable when configuring fallback

 @SentinelResource(value = "testSentinelFallback", fallback = "myFallback")
    public String testSentinelFallback() {
        if (Boolean. TRUE) {
            throw new BusinessException(FacadeResultCodeEnum.BAD_PARAMS);
        }
        return "return success ok";
    }

    public String myFallback(Throwable throwable) {
        if (throwable instanceof BlockException) {
            return "Enter myFallback logic current limit exception";
        }
        return "Enter myFallback logic business exception";
    }

Execution result: When the interface is limited or the interface has a business exception, it will enter the fallback downgrade method; and the exception type can be used to distinguish between the current limit exception and the business exception;

call return result [result=enter myFallback logic business exception]

Call return result [result=enter myFallback logic current limit exception]
Call return result [result=enter myFallback logic current limit exception]
...

4. Configure fallback and blockHandler at the same time

 @SentinelResource(value = "testSentinelFallback", fallback = "myFallback", blockHandler = "myBlockHandler")
    public String testSentinelFallback() {
        if (Boolean. TRUE) {
            throw new BusinessException(FacadeResultCodeEnum.BAD_PARAMS);
        }
        return "return success ok";
    }

    public String myFallback(Throwable throwable) {
        if (throwable instanceof BlockException) {
            return "Enter myFallback logic current limit exception";
        }
        return "Enter myFallback logic business exception";
    }

    public String myBlockHandler(BlockException blockException) {
        return "Enter myBlockHandler logic";
    }

Execution result: If both blockHandler and fallback are configured, when the flow limit is not triggered and the method logic throws a business exception, it will enter the fallback method; when the flow limit is triggered, the method logic cannot be entered, and BlockException is directly thrown into the blockHandler method;

5. Conclusion

5.1 Exception capture logic

1. blockHandler

  • blockHandler only handles current-limit exceptions;
  • When using blockHandler, the method signature parameter is consistent with the original method, and the BlockException parameter must be added at the last position of the parameter;
  • If the BlockException parameter is not added, it will not take effect;

2. fallback

  • fallback can handle all types of exceptions, including current limit exceptions and business exceptions;
  • When using fallback, the method signature parameter can be exactly the same as the original method, or it is also acceptable to add the Throwable parameter at the last position of the parameter;
  • Distinguish whether it is a current-limiting exception or other exceptions by the type of the Throwable parameter;
  • When blockHandler and fallback are in effect at the same time, the current limit exception will be handled by blockHandler first and will no longer enter the fallback logic;
5.2 Reasonable code writing

(1) Configure effective blockHandler and fallback to handle current limit exception and business exception respectively

 @SentinelResource(value = "testSentinelFallback", fallback = "myFallback", blockHandler = "myBlockHandler")
    public String testSentinelFallback() {
        //...
        return "return success ok";
    }

    public String myFallback(Throwable throwable) {
        return "Enter myFallback logic business exception";
    }

    public String myBlockHandler(BlockException blockException) {
        return "Enter myBlockHandler logic";
    }

(2) Only configure fallback and distinguish between current limit exception and business exception by Throwable type

 @SentinelResource(value = "testSentinelFallback", fallback = "myFallback")
    public String testSentinelFallback() {
        //...
        return "return success ok";
    }

    public String myFallback(Throwable throwable) {
        if (throwable instanceof com.alibaba.csp.sentinel.slots.block.flow.FlowException) {
            final FlowRule rule = ((FlowException) throwable). getRule();
            final double count = rule. getCount();
            final String resource = rule. getResource();
            // Print current limiting rule information
            log.warn("testSentinelFallback triggers current limit downgrade [sentinelResource={} QpsLimit={}]]", resource, count);
            return null;
        } else {
            log.warn("testSentinelFallback triggers an abnormal downgrade and throws an exception", throwable);
            throw new RuntimeException("testSentinelFallback business exception");
        }
    }
5.3 Annotation parameter interpretation and precautions

1. @SentinelResource annotation parameter description

Attribute Default Value Description
blockHandler

It is used to degrade processing logic when exceptions such as current limit/fuse/system protection are thrown, blockHandler is aimed at BlockException type exceptions, and has a higher priority than fallback

The access scope of the blockHandler function needs to be public, the return type needs to match the original method, the parameter type needs to match the original method and an additional parameter is added at the end, the type is BlockException;

The blockHandler function needs to be in the same class as the original method by default;

blockHandlerClass If you want to use functions of other classes, you can specify blockHandlerClass as the corresponding class Class object, note that the corresponding function must be a static function, otherwise it cannot be parsed;
defaultFallback The name of the default fallback function , optional, usually used for general fallback logic (that is, can be used for many services or methods);

The default fallback function can handle all types of exceptions (except the exception types excluded in exceptionsToIgnore);

If both fallback and defaultFallback are configured, only fallback will take effect

entryType EntryType.OUT The type of traffic called by the resource is the entry traffic (EntryType.IN) It is still export traffic (EntryType.OUT), note that the system protection rules are only valid for IN
exceptionsToIgnore is used to specify which The exception is excluded, it will not be included in the exception statistics, nor will it enter the fallback logic, but will be thrown as it is; the priority is higher than exceptionsToTrace
exceptionsToTrace Throwable.class is used to specify which exceptions are not excluded; if it belongs to this type, it will be included in the exception statistics, and will also enter the fallback logic, and will not be thrown as it is; no It is recommended to modify the default value;
fallback

Used to provide fallback processing logic when an exception is thrown; fallback is for all types of exceptions (except the exception types excluded in exceptionsToIgnore)

The method parameter list needs to be consistent with the original function, or an additional Throwable type parameter can be added to receive the corresponding exception (note that it is different from the BlockException added by blockHandler)

fallbackClass similar blockHandlerClass parameter
resourceType resource type, default 0
value resource name, required

2. Distinguish current limit abnormality and fusing abnormality

An exception will be thrown in the current limiting state: FlowException (inherited from BlockException)

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.alibaba.csp.sentinel.slots.block.flow.FlowException] with root cause
 
com.alibaba.csp.sentinel.slots.block.flow.FlowException: null

The fuse state will throw an exception: DegradeException (inherited from BlockException)

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.alibaba.csp.sentinel.slots.block.degrade.DegradeException] with root cause
 
com.alibaba.csp.sentinel.slots.block.degrade.DegradeException: null

Reference: annotation-support | Sentinel