Spring Cloud Alibaba Sentinel service fusing and current limiting

Spring Cloud Alibaba is committed to providing a one-stop solution for microservice development. Sentinel, as one of its core components, has a series of service protection functions such as fusing and current limiting. This paper will introduce its usage in detail.

Sentinel introduction

With the popularity of microservices, the stability between services and services becomes more and more important. Sentinel takes the flow as the starting point to protect the stability of services from multiple dimensions such as flow control, fuse degradation and system load protection.

Sentinel has the following features:

  • Rich application scenarios: it has undertaken the core scenarios of Alibaba's double 11 traffic promotion in recent 10 years, such as seckill, which can fuse the unavailable downstream applications in real time;
  • Complete real-time monitoring: provide real-time monitoring function at the same time. You can see the second level data of a single machine connected to the application in the console, and even the summary operation of clusters with a scale of less than 500;
  • Extensive open source Ecology: provide out of the box integration modules with other open source frameworks / libraries, such as integration with Spring Cloud, Dubbo and gRPC;
  • Perfect SPI extension points: provide simple, easy-to-use and perfect SPI extension points. You can quickly customize logic by implementing extension points.

Installing Sentinel console

Sentinel console is a lightweight console application. It can be used to view single machine resource monitoring and cluster resource summary in real time, and provides a series of rule management functions, such as flow control rules, degradation rules, hotspot rules, etc.

Let's download Sentinel-dashboard-1.6.3 from the official website first Jar file, download address: github.com/alibaba/Sen...

After downloading, enter the following command on the command line to run Sentinel console:

$ java -jar sentinel-dashboard-1.6.3.jar

Sentinel console runs on port 8080 by default. The login account and password are sentinel, which can be accessed through the following address: http://localhost:8080

Sentinel console can view the real-time monitoring data of a single machine:

Create project module

Here we create a Sentinel service module to demonstrate the fusing and current limiting functions of Sentinel.

In POM Add related dependencies in XML. Here we use Nacos as the registry, so we need to add Nacos dependencies at the same time:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

In application Add relevant configurations to YML, mainly to configure the address of Nacos and Sentinel console:

server:
  port: 8401
spring:
  application:
    name: sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Configure Nacos address
    sentinel:
      transport:
        dashboard: localhost:8080 #Configure sentinel dashboard address
        port: 8719
service-url:
  user-service: http://nacos-user-service
management:
  endpoints:
    web:
      exposure:
        include: '*'

Current limiting function

Sentinel Starter provides flow limiting embedding points for all HTTP services by default. We can also define some flow limiting behaviors by using @ SentinelResource.

Create RateLimitController class

Test fusing and current limiting functions.

@RestController
@RequestMapping("/rateLimit")
public class RateLimitController {

    /**
     * To limit the flow by resource name, you need to specify the flow limiting processing logic
     */
    @GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public CommonResult byResource() {
        return new CommonResult("Limit current by resource name", 200);
    }

    /**
     * Flow restriction by URL, with default flow restriction processing logic
     */
    @GetMapping("/byUrl")
    @SentinelResource(value = "byUrl",blockHandler = "handleException")
    public CommonResult byUrl() {
        return new CommonResult("Press url Current limiting", 200);
    }

    public CommonResult handleException(BlockException exception){
        return new CommonResult(exception.getClass().getCanonicalName(),200);
    }

}

Limit current according to resource name

We can limit the current according to the value (resource name) defined in the @ SentinelResource annotation, but we need to specify the current limiting processing logic.

Flow control rules can be configured on Sentinel console. Since we use the Nacos registry, we start Nacos and Sentinel service first. Due to the lazy loading rule adopted by Sentinel, we need to access the following interface first before the corresponding service information can be found in Sentinel console. We need to access the following interface first: http://localhost:8401/rateLimit/byResource

Configure flow control rules on Sentinel console according to the value value annotated by @ SentinelResource:

The information of the above defined access flow can be found and processed quickly:

Limit flow according to URL

We can also limit the flow by accessing the URL, and the default flow limiting processing information will be returned. Configure flow control rules on Sentinel console and use the accessed URL:

If the interface is accessed multiple times, the default current limiting processing result will be returned: http://localhost:8401/rateLimit/byUrl

Custom current limiting processing logic

We can customize the general current limiting processing logic and specify it in @ SentinelResource.

Create a CustomBlockHandler class to customize the flow limiting processing logic:

public class CustomBlockHandler {

    public CommonResult handleException(BlockException exception){
        return new CommonResult("Custom current limiting information",200);
    }
}

Use custom current limiting processing logic in RateLimitController:

@RestController
@RequestMapping("/rateLimit")
public class RateLimitController {

    /**
     * Custom general current limiting processing logic
     */
    @GetMapping("/customBlockHandler")
    @SentinelResource(value = "customBlockHandler", blockHandler = "handleException",blockHandlerClass = CustomBlockHandler.class)
    public CommonResult blockHandler() {
        return new CommonResult("Current limiting successful", 200);
    }

}

Fusing function

Sentinel supports the protection of calls between services and the fusing operation of fault applications. Here, we use RestTemplate to call the interface provided by Nacos user service to demonstrate this function.

First, we need to wrap the RestTemplate instance with @ SentinelRestTemplate:

@Configuration
public class RibbonConfig {

    @Bean
    @SentinelRestTemplate
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

Add the CircleBreakerController class and define the call to the interface provided by Nacos user service:

@RestController
@RequestMapping("/breaker")
public class CircleBreakerController {

    private Logger LOGGER = LoggerFactory.getLogger(CircleBreakerController.class);
    @Autowired
    private RestTemplate restTemplate;
    @Value("${service-url.user-service}")
    private String userServiceUrl;

    @RequestMapping("/fallback/{id}")
    @SentinelResource(value = "fallback",fallback = "handleFallback")
    public CommonResult fallback(@PathVariable Long id) {
        return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
    }

    @RequestMapping("/fallbackException/{id}")
    @SentinelResource(value = "fallbackException",fallback = "handleFallback2", exceptionsToIgnore = {NullPointerException.class})
    public CommonResult fallbackException(@PathVariable Long id) {
        if (id == 1) {
            throw new IndexOutOfBoundsException();
        } else if (id == 2) {
            throw new NullPointerException();
        }
        return restTemplate.getForObject(userServiceUrl + "/user/{1}", CommonResult.class, id);
    }

    public CommonResult handleFallback(Long id) {
        User defaultUser = new User(-1L, "defaultUser", "123456");
        return new CommonResult<>(defaultUser,"Service degradation return",200);
    }

    public CommonResult handleFallback2(@PathVariable Long id, Throwable e) {
        LOGGER.error("handleFallback2 id:{},throwable class:{}", id, e.getClass());
        User defaultUser = new User(-2L, "defaultUser2", "123456");
        return new CommonResult<>(defaultUser,"Service degradation return",200);
    }
}

Start the Nacos user service and sentinel service services. Since we do not define a user with id 4 in the Nacos user service, all the following interfaces will return service degradation results: http://localhost:8401/breaker/fallback/4

{
	"data": {
		"id": -1,
		"username": "defaultUser",
		"password": "123456"
	},
	"message": "Service degradation return",
	"code": 200
}

Because we used the exceptionsToIgnore parameter and ignored the NullPointerException, the service degradation will not occur when our provider reports null pointers: http://localhost:8401/breaker/fallbackException/2

Use in combination with Feign

Sentinel also adapts Feign component. We can also use Feign to call between services.

First, we need to be in POM Add Feign related dependencies to XML:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

In application Open Sentinel support for Feign in YML:

feign:
  sentinel:
    enabled: true #Open sentinel support for feign

Add @ EnableFeignClients to the application startup class to start Feign;

Create a UserService interface to define the call to the Nacos user service service:

@FeignClient(value = "nacos-user-service",fallback = UserFallbackService.class)
public interface UserService {
    @PostMapping("/user/create")
    CommonResult create(@RequestBody User user);

    @GetMapping("/user/{id}")
    CommonResult<User> getUser(@PathVariable Long id);

    @GetMapping("/user/getByUsername")
    CommonResult<User> getByUsername(@RequestParam String username);

    @PostMapping("/user/update")
    CommonResult update(@RequestBody User user);

    @PostMapping("/user/delete/{id}")
    CommonResult delete(@PathVariable Long id);
}

Create a UserFallbackService class to implement the UserService interface, which is used to handle the service degradation logic:

@Component
public class UserFallbackService implements UserService {
    @Override
    public CommonResult create(User user) {
        User defaultUser = new User(-1L, "defaultUser", "123456");
        return new CommonResult<>(defaultUser,"Service degradation return",200);
    }

    @Override
    public CommonResult<User> getUser(Long id) {
        User defaultUser = new User(-1L, "defaultUser", "123456");
        return new CommonResult<>(defaultUser,"Service degradation return",200);
    }

    @Override
    public CommonResult<User> getByUsername(String username) {
        User defaultUser = new User(-1L, "defaultUser", "123456");
        return new CommonResult<>(defaultUser,"Service degradation return",200);
    }

    @Override
    public CommonResult update(User user) {
        return new CommonResult("The call failed and the service was degraded",500);
    }

    @Override
    public CommonResult delete(Long id) {
        return new CommonResult("The call failed and the service was degraded",500);
    }
}

Use UserService in userfeign controller to call the interface in Nacos user service through Feign:

@RestController
@RequestMapping("/user")
public class UserFeignController {
    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public CommonResult getUser(@PathVariable Long id) {
        return userService.getUser(id);
    }

    @GetMapping("/getByUsername")
    public CommonResult getByUsername(@RequestParam String username) {
        return userService.getByUsername(username);
    }

    @PostMapping("/create")
    public CommonResult create(@RequestBody User user) {
        return userService.create(user);
    }

    @PostMapping("/update")
    public CommonResult update(@RequestBody User user) {
        return userService.update(user);
    }

    @PostMapping("/delete/{id}")
    public CommonResult delete(@PathVariable Long id) {
        return userService.delete(id);
    }
}

Calling the following interface will cause service degradation and return service degradation processing information: http://localhost:8401/user/4

{
	"data": {
		"id": -1,
		"username": "defaultUser",
		"password": "123456"
	},
	"message": "Service degradation return",
	"code": 200
}

Storing rules using Nacos

By default, when we configure rules in Sentinel console, the console pushes the rules to the client through API and updates them directly to memory. Once we restart the application, the rules will disappear. Next, let's introduce how to persist the configuration rules, taking storing them in Nacos as an example.

Schematic diagram

First, we create rules directly in the configuration center, which pushes the rules to the client. Sentinel console also obtains configuration information from the configuration center.

Function demonstration

First in POM Add related dependencies to XML:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

Modify application YML configuration file, add Nacos data source configuration:

spring:
  cloud:
    sentinel:
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-sentinel
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

Add configuration in Nacos:

Add configuration information as follows:

[
    {
        "resource": "/rateLimit/byUrl",
        "limitApp": "default",
        "grade": 1,
        "count": 1,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]

Explanation of relevant parameters:

  • Resource: resource name;
  • limitApp: source application;
  • grade: threshold type, 0 indicates the number of threads, 1 indicates QPS;
  • count: single machine threshold;
  • strategy: flow control mode, 0 indicates direct, 1 indicates association, and 2 indicates link;
  • controlBehavior: flow control effect, 0 indicates rapid failure, 1 indicates Warm Up, and 2 indicates queuing;
  • clusterMode: whether to cluster.

It is found that Sentinel console has the following current limiting rules:

By accessing the test interface quickly, you can find that the current limiting processing information is returned:

Refer to the official Spring Cloud Alibaba documentation: github.com/alibaba/spr...

For more dry goods, please move to: https://antoniopeng.com

Tags: Spring Cloud

Posted by alwaysme on Tue, 19 Apr 2022 06:38:03 +0930