In the previous article “Using Sentinel to achieve Interface Flow Limiting”, we only encapsulated spring-cloud-starter-Alibaba-Sentinel by introducing the integration of Spring Cloud Alibaba to Sentinel. This completes the flow limiting control for all Spring MVC interfaces. In practice, however, we may need to limit the flow beyond the interface. It may be desirable to control the call limiting of a method or the call limiting of an external resource. At this time, we have to manually define the resource points that need to limit traffic, and configure related traffic limiting policies and other content.

In today’s article, we’ll take a look at how to use @SentinelResource annotations to flexibly define control resources and how to configure control policies.

Customize resource points

The following examples are based on the fact that you have already introduced Spring Cloud Alibaba Sentinel. If you do not already know these, you are advised to read “Implementing Interface Traffic Limiting with Sentinel” first.

Step 1: Add annotation support to the main application class:

@SpringBootApplication
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }

    // Annotate the supported configuration beans
    @Bean
    public SentinelResourceAspect sentinelResourceAspect(a) {
        return newSentinelResourceAspect(); }}Copy the code

Step 2: Use the @SentinelResource annotation where Sentinel is needed to control traffic, such as the following example of a method that controls the Service logic layer:

@Slf4j
@Service
public class TestService {

    @SentinelResource(value = "doSomeThing")
    public void doSomeThing(String str) { log.info(str); }}Copy the code

At this point the definition of a method that needs to be protected is complete. Let’s talk about how we implement different protection policies, including traffic limiting and degradation, after defining resource points.

How to realize current limiting and fusing downgrading

After defining resource points, we can use Dashboard to set limiting and degrading policies to protect resource points. You can also use @SentinelResource to specify an exception handling strategy when limiting traffic and degradation occur. Let’s take a look at how traffic limiting and degradation are implemented.

Realize current limiting control

Step 1: Call the protected method at the Web layer:


@RestController
public class TestController {

    @Autowired
    private TestService testService;

    @GetMapping("/hello")
    public String hello(a) {
        estService.doSomeThing("hello " + new Date());
        return "didispace.com"; }}Copy the code

Step 2: Start the test application and start Sentinel-Dashboard. Send a request to the/Hello interface so that the Sentinel-Dashboard can see several control points as shown below:

As you can see, there is a doSomeThing resource point in addition to the/Hello resource point as in the previous getting started example. You can set traffic limiting rules for this resource point through the interface, such as setting its QPS to 2. Since the/Hello resource does not set traffic limiting rules, you can directly simulate calling the doSomeThing resource as long as you request the/Hello interface to observe whether the traffic limiting rules take effect.

You can use any tool you like to call the/Hello interface, as long as the QPS exceeds 2, then the following error will be returned, indicating that the traffic limiting policy is in effect.

In this case, the console on the server also has the corresponding traffic limiting error log:

The 2019-06-27 11:30:43. 36898-514 the INFO [nio - 8001 - exec - 3] C.D.A.S entinel. Service. TestService: [.[/].[dispatcherServlet] : servlet.service ()for servlet [dispatcherServlet] incontext with path [] threw exception [Request processing failed;  nested exception is java.lang.reflect.UndeclaredThrowableException] with root cause com.alibaba.csp.sentinel.slots.block.flow.FlowException: nullCopy the code

Realize exception handling of current limiting

By default, Sentinel limits the flow of a control resource by directly throwing an exception, which is the log content posted in the previous section. In the case of no reasonable business continuity or front-end docking, this can be done, but in normal circumstances, in order to better user business, some special processing will be implemented after limiting the flow, we do not want to show a blunt error. Then you just need to do some processing based on the above example, for example:

@Slf4j
@Service
public class TestService {

    @SentinelResource(value = "doSomeThing", blockHandler = "exceptionHandler")
    public void doSomeThing(String str) {
        log.info(str);
    }

    // Handle traffic limiting and blocking
    public void exceptionHandler(String str, BlockException ex) {
        log.error( "blockHandler:"+ str, ex); }}Copy the code

Two main things were done:

  • through@SentinelResourceannotationsblockHandlerProperty specifies the specific handler function
  • Implement the handler function, which must take the same arguments as the resource point, and finally addBlockExceptionAbnormal parameter; Also, the return type must be the same.

If you are familiar with Hystrix, you will find that this design is similar to the HystrixCommand definition of fallback, which is easy to understand.

After the preceding changes are complete, the interface is accessed again (note that traffic limiting rules must be configured). In this case, the front end does not return exception information, and the back end prints the log output defined in exceptionHandler. In the actual application, as long as according to the business needs to do the traffic limiting request cache or front-end prompt can be based on this method to achieve.

Achieve circuit breaker downgrade

In addition to being used for limiting traffic, the @SentinelResource annotation also implements a circuit-downgrading strategy similar to Hystrix’s. Here’s how to use it.

Step 1: As with limiting traffic, use the @SentinelResource annotation to mark resource points, such as:

@Slf4j
@Service
public class TestService {

    @SentinelResource(value = "doSomeThing2")
    public void doSomeThing2(String str) {
        log.info(str);
        throw new RuntimeException("Abnormal"); }}Copy the code

Here you create a new method in the TestService class and name the resource doSomeThing2 using @SentinelResource. This method throws exceptions in conjunction with the subsequent development of a demotion strategy based on the proportion of exceptions (similar to Hystrix). Sentinel is richer than Hystrix and has a downgrading strategy based on response time and number of outliers.

Step 2: Call the protected method at the Web layer:

@RestController
public class TestController {

    @Autowired
    private TestService testService;

    @GetMapping("/hello2")
    public String hello2(a) {
        testService.doSomeThing2("hello2 " + new Date());
        return "didispace.com"; }}Copy the code

Step 3: Start the test application and start Sentinel-Dashboard. Send a request to the /hello2 interface so that a resource point named doSomeThing2 is visible on the Sentinel-Dashboard. Then click the “Degrade” button to set the degrade rule for the resource. The exception ratio policy is used here, with the ratio set to 0.5 (i.e., 50% exception rate) and the time window set to 2 (seconds).

When a doSomeThing2 method is called with QPS >= 5 and the exception rate is greater than 50%, subsequent calls within 2 seconds will initiate a Fuse degrade according to the demotion policy configuration above. By default, a DegradeException will be thrown.

2019-06-27 17:49:58.913 ERROR 99863 --- [nio-8001-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() forservlet [dispatcherServlet] in context with path [] threw exception [Request processing failed;  nested exception is java.lang.reflect.UndeclaredThrowableException] with root cause com.alibaba.csp.sentinel.slots.block.degrade.DegradeException:null
Copy the code

Downgrading of fusing

It is very simple to define the degradation treatment of fuses in Sentinel, very similar to Hystrix. Just use the fallback attribute of the @SentinelResource annotation to specify the specific method name. It is also important to note that the pass-participation returns must be consistent. Such as:

@Slf4j
@Service
public class TestService {

    // Fuses and downgrades
    @SentinelResource(value = "doSomeThing2", fallback = "fallbackHandler")
    public void doSomeThing2(String str) {
        log.info(str);
        throw new RuntimeException("Abnormal");
    }

    public void fallbackHandler(String str) {
        log.error("fallbackHandler:"+ str); }}Copy the code

After completing the above modification, restart the application and set the Fuse degrade policy for doSomeThing2 resources (using the exception percentage), then frequently request/Hello2 interface. After QPS>=5, the interface must meet the fusing degradation condition because it keeps throwing exceptions. In this case, the fallbackHandler method will be executed to print the following log:

2019-06-27 23:44:19.432 ERROR 58471 --- [nio-8001-exec-1] C.D.A.S entinel. Service. TestService: fallbackHandler: hello2 Thu Jun27 23:44:19 CST 2019
2019-06-27 23:44:19.599 ERROR 58471 --- [nio-8001-exec-2] C.D.A.S entinel. Service. TestService: fallbackHandler: hello2 Thu Jun27 23:44:19 CST 2019
2019-06-27 23:44:19.791 ERROR 58471 --- [nio-8001-exec-3] C.D.A.S entinel. Service. TestService: fallbackHandler: hello2 Thu Jun27 23:44:19 CST 2019
2019-06-27 23:44:19.975 ERROR 58471 --- [nio-8001-exec-4] C.D.A.S entinel. Service. TestService: fallbackHandler: hello2 Thu Jun27 23:44:19 CST 2019
2019-06-27 23:44:20.168 ERROR 58471 --- [nio-8001-exec-5] C.D.A.S entinel. Service. TestService: fallbackHandler: hello2 Thu Jun27 23:44:20 CST 2019
Copy the code

More annotation properties

That’s all for specific use cases on the two main uses of the @SentinelResource annotation: limiting traffic control and fuse downgrading. In addition, the annotations also have some more refined configurations, such as ignoring certain exceptions, default degradation functions, and so on, as described below:

  • value: Resource name, required (cannot be empty)
  • entryType: Entry type. This parameter is optionalEntryType.OUT)
  • blockHandler / blockHandlerClass: blockHandlerCorresponding processingBlockExceptionThe optional function name of the The access range of the blockHandler function must bepublic, the return type needs to match the original method, and the parameter type needs to match the original method and add an extra parameter at the end, of typeBlockException. The blockHandler function needs to be in the same class as the original method by default. You can specify if you want to use a function from another classblockHandlerClassOf the corresponding classClassObject, note that the corresponding function must be static, otherwise it cannot be parsed.
  • fallback: The optional fallback function name that provides fallback handling logic when an exception is thrown. The fallback function is available for all types of exceptions (exceptexceptionsToIgnoreThe type of exception that is excluded). Fallback function signature and position requirements:
    • The return value type must be the same as that of the original function;
    • The method argument list needs to be the same as the original function, or it can be an extra oneThrowableType to receive the corresponding exception.
    • The fallback function needs to be in the same class as the original method by default. You can specify if you want to use a function from another classfallbackClassOf the corresponding classClassObject, note that the corresponding function must be static, otherwise it cannot be parsed.
  • defaultFallback(since 1.6.0) : Default fallback function name, optional, usually used for generic fallback logic (that is, can be used for many services or methods). The default fallback function can be used for all types of exceptions (exceptexceptionsToIgnoreThe type of exception that is excluded). If both fallback and defaultFallback are configured, only fallback takes effect. DefaultFallback signature requirements:
    • The return value type must be the same as that of the original function;
    • The method parameter list needs to be empty, or you can have an extra parameterThrowableType to receive the corresponding exception.
    • DefaultFallback needs to be in the same class as the original method by default. You can specify if you want to use a function from another classfallbackClassOf the corresponding classClassObject, note that the corresponding function must be static, otherwise it cannot be parsed.
  • exceptionsToIgnore(since 1.6.0) : Specifies which exceptions are excluded, are not counted in the exception count, and are not included in fallback logic, but are thrown as is.

Note: The fallback function before 1.6.0 only handles DegradeException, not service exception.

In particular, if blockHandler and Fallback are configured, only blockHandler processing logic will be entered when a BlockException is thrown due to traffic limiting demotion. If blockHandler, FallBack, and defaultFallback are not configured, BlockException will be thrown when traffic limiting is degraded.

Reference: Sentinel official documentation

Version description: This article is based on spring-cloud-Alibaba-Dependencies version 0.2.2. If you encounter special problems, please check whether the version is consistent or directly refer to code examples to check specific cases.

Code sample

This article introduces the client code for the content. Sample readers can look at the Alibaba-Sentinel-Annotation project in the repository below:

  • Making:Github.com/dyc87112/Sp…
  • Gitee:Gitee.com/didispace/S…

If you are interested in these, welcome to star, follow, favorites, forward to give support!