Sentinel is a flow control component of Alibaba open source oriented to distributed service architecture. It mainly takes flow as the entry point and guarantees the stability of micro-service from multiple dimensions such as flow control, fuse downgrading and system adaptive protection.

In addition to the Spring Cloud Gateway official RequestRateLimiterGatewayFilterFactory filter factory to implement the Gateway current limit, we can also choose to integrate Sentinel to realize current limit.

In this article, we will learn the basic knowledge of Sentinel integrated with Spring Cloud Gateway. You can refer to Sentinel official Documentation (SentinelGuard.io).

Without further ado, let’s begin today’s lesson.

Sentinel current-limiting

Beginning with version 1.6.0, Sentinel provides adaptive modules for Spring Cloud Gateway, which can provide flow limiting for two resource dimensions:

  • Route dimension: the route entry configured in the Spring configuration file with the corresponding routeId resource name
  • Custom API dimensions: Users can customize some API groups using the API provided by Sentinel

1. Add dependencies

<! -- SpringCloud Alibaba Sentinel -->
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
		
<! -- SpringCloud Alibaba Sentinel Gateway -->
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
Copy the code

2. Custom flow limiting exception processing class

/** * Custom traffic limiting exception handling **@author ezhang
 */
public class SentinelFallbackHandler implements WebExceptionHandler {

    private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) {
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        serverHttpResponse.getHeaders().add("Content-Type"."application/json; charset=UTF-8");
        byte[] datas = "{"code":429, "msg":"The request exceeded the maximum number, please try again later"}".getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);
        return serverHttpResponse.writeWith(Mono.just(buffer));
    }

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {

        if (exchange.getResponse().isCommitted()) {
            return Mono.error(ex);
        }
        if(! BlockException.isBlockException(ex)) {return Mono.error(ex);
        }
        return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange));
    }

    private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) {
        returnGatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable); }}Copy the code

This class implements the exception handling interface WebExceptionHandler in Spring WebFlux.

This class in addition to the custom, also can be used directly Sentinel own SentinelGatewayBlockExceptionHandler class.

public class SentinelGatewayBlockExceptionHandler implements WebExceptionHandler {
    private List<ViewResolver> viewResolvers;
    privateList<HttpMessageWriter<? >> messageWriters;private final Supplier<Context> contextSupplier = () -> {
        return new Context() {
            publicList<HttpMessageWriter<? >> messageWriters() {return SentinelGatewayBlockExceptionHandler.this.messageWriters;
            }

            public List<ViewResolver> viewResolvers(a) {
                return SentinelGatewayBlockExceptionHandler.this.viewResolvers; }}; };public SentinelGatewayBlockExceptionHandler(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolvers;
        this.messageWriters = serverCodecConfigurer.getWriters();
    }

    private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) {
        return response.writeTo(exchange, (Context)this.contextSupplier.get());
    }

    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        if (exchange.getResponse().isCommitted()) {
            return Mono.error(ex);
        } else {
            return! BlockException.isBlockException(ex) ? Mono.error(ex) :this.handleBlockedRequest(exchange, ex).flatMap((response) -> {
                return this.writeResponse(response, exchange); }); }}private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) {
        returnGatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable); }}Copy the code

SentinelGatewayBlockExceptionHandler class is also realized WebExceptionHandler interface.

3. Traffic limiting rule configuration class

/** * Traffic limiting rule configuration class **@author ezhang
 */

@Configuration
public class GatewayConfig {

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelFallbackHandler sentinelGatewayExceptionHandler(a) {
        return new SentinelFallbackHandler();
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter(a) {

        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void doInit(a) {
        // Load the gateway traffic limiting rule
        initGatewayRules();
    }

    /** * Gateway traffic limiting rule */
    private void initGatewayRules(a) {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("cloud-gateway")
                .setCount(3)    // Traffic limiting threshold
                .setIntervalSec(60));   // Statistics time window, in seconds, default is 1 second
        // Load the gateway traffic limiting ruleGatewayRuleManager.loadRules(rules); }}Copy the code

4. Application. Yml configuration

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: cloud-gateway
          uri: http://192.168.1.211:8088
          predicates:
            - Path=/ytb/**
          filters:
            - StripPrefix=1
Copy the code

5. Start the test

Start the project after we visit this interface at http://localhost:8080/ytb/file/getFileList

At this point, the return is normal, but when we access the fourth time within a minute, an exception will be returned, indicating that the traffic limiting is successful.

We can also run this command from CMD to view real-time statistics

curl http://localhost:8719/cnode? id=cloud-gateway

The output content is in the following format:

Among them:

  • Thread: indicates the number of threads currently processing the resource.
  • Pass: requests that arrive within one second;
  • Blocked: represents the number of requests controlled by traffic in a second;
  • Success: represents the request that was successfully processed within one second;
  • Total: indicates the total number of incoming requests and blocked requests within one second.
  • RT: indicates the average response time of the resource in one second.
  • 1m-pass: requests that arrive within a minute;
  • 1m-block: the request is blocked within one minute.
  • 1m-all: indicates the sum of incoming requests and blocked requests within one minute.
  • Exception: indicates the total number of service exceptions in one second.

In addition, whether they trigger limiting, fusing downscaling or system protection, their second level intercept details are logged in ${user_HOME}/logs/ CSP/sentinel-block-.log. If no interception occurs, the log does not appear. The log format is as follows:

The 2021-12-24 15:46:02 | 1 | cloud - gateway, ParamFlowException, $D, | 2, 0, 2021-12-24 15:46:03 | 1 | cloud - gateway, ParamFlowException, $D | 1, 0Copy the code

Description:

index example instructions
1 The 2021-12-24 15:46:03 The time stamp
2 1 The first resource that occurs in this second
3 cloud-gateway The name of the resource
4 XXXException The reason for interception, usuallyFlowExceptionIs blocked by traffic limiting rules,DegradeExceptionMeans demoted,SystemBlockExceptionIt is intercepted by the system protection
5 2, 0 2 number of intercepts, 0 is meaningless and negligible

Sentinel packet current limiting

Configure traffic limiting for cloud-system and Cloud-YTB groups

1.application.ymlThe configuration file

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: cloud-ytb
          uri: http://192.168.1.211:8088
          predicates:
          - Path=/ytb/**
          filters:
          - StripPrefix=1
        - id: cloud-system
          uri: http://192.168.1.211:8088
          predicates:
          - Path=/system/**
          filters:
          - StripPrefix=1
Copy the code

2. Traffic limiting rule configuration class

/** * Traffic limiting rule configuration class **@author ezhang
 */

@Configuration
public class GatewayConfig {

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelFallbackHandler sentinelGatewayExceptionHandler(a) {
        return new SentinelFallbackHandler();
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter(a) {

        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void doInit(a) {
        / / group
        initCustomizedApis();
        // Load the gateway traffic limiting rule
        initGatewayRules();
    }

    /** * Gateway traffic limiting rule */
    private void initGatewayRules(a) {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("system-api")
                .setCount(3)    // Traffic limiting threshold
                .setIntervalSec(60));   // Statistics time window, in seconds, default is 1 second
        rules.add(new GatewayFlowRule("ytb-api")
                .setCount(6)    // Traffic limiting threshold
                .setIntervalSec(60));
        // Load the gateway traffic limiting rule
        GatewayRuleManager.loadRules(rules);
    }

    private void initCustomizedApis(a) {
        Set<ApiDefinition> definitions = new HashSet<>();
        / / cloud - system group
        ApiDefinition api1 = new ApiDefinition("system-api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {
                    {
                        // Matches /file with all requests for its subpath
                        add(new ApiPathPredicateItem().setPattern("/system/file/**") .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)); }});/ / cloud - ytb group
        ApiDefinition api2 = new ApiDefinition("ytb-api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {
                    {
                        // Matches only /file/getFileList
                        add(new ApiPathPredicateItem().setPattern("/ytb/file/getFileList")); }}); definitions.add(api1); definitions.add(api2); GatewayApiDefinitionManager.loadApiDefinitions(definitions); }}Copy the code

Visit: http://localhost:8080/system/file/getFileList (trigger current limiting access: http://localhost:8080/system/user/list (not trigger current limiting access: http://localhost:8080/ytb/file/getFileList (trigger current limiting access: http://localhost:8080/ytb/file/updateFileInfo (not trigger current limit)

Sentinel Custom exception

Sentinel supports custom exception handling.

Solution 1: YML configuration

# Spring
spring: 
  cloud:
    sentinel:
      scg:
        fallback:
          mode: response
          response-body: '{"code":403," MSG ":" request exceeded the maximum number, please try again later "}'
Copy the code

After starting the test to trigger flow limiting:

Solution 2: The custom SentinelFallbackHandler is injected into GatewayConfig

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelFallbackHandler sentinelGatewayExceptionHandler(a) {
    return new SentinelFallbackHandler();
}
Copy the code

Custom exception can make the trigger current limit returns information more neat, if we directly use the Sentinel’s own SentinelGatewayBlockExceptionHandler class after injection into the GatewayConfig then trigger current limit returns such exception information

Here’s a quick look at how the Spring Cloud Gateway integration Sentinel is configured to implement traffic limiting. There is no in-depth and systematic study of Sentinel. In the future, we will learn about Sentinel fuse downgrading.

Now that the Spring Cloud Gateway series is over, I’m ready to start Nacos.