Combination of Nacos and Sentinel to achieve traffic safety control

Alibaba Sentinel is a high-performance and lightweight solution for flow control and fuse degradation. It is a highly available flow control component for distributed service architecture.

Sentinel website: https://sentinelguard.io/zh-cn/

  Github: https://github.com/alibaba/Sentinel

  

What is Sentinel

  

With the popularity of microservices, the stability between services becomes more and more important. Sentinel mainly takes traffic as the entry point, and guarantees the stability of microservice from traffic control, fuse degradation, system adaptive protection and other dimensions.

  

Sentinel has the following characteristics:

  • Rich application scenarios: Sentinel has undertaken the core scenarios of Alibaba's "double 11" traffic promotion in the past 10 years, such as seckill (i.e. sudden flow control within the range of system capacity), message peak cutting and valley filling, cluster flow control, real-time fuse downstream unavailable application, etc.
  • Complete real-time monitoring: Sentinel also provides real-time monitoring function. You can see the second level data of a single machine accessing the application in the console, or even the summary operation of clusters of less than 500.
  • Wide open source ecosystem: Sentinel provides out of the box integration modules with other open source frameworks / libraries, such as integration with Spring Cloud, Dubbo, gRPC. You only need to introduce the corresponding dependency and make simple configuration to access Sentinel quickly.
  • Perfect SPI extension point: Sentinel provides simple and easy-to-use, perfect SPI extension interface. You can quickly customize the logic by implementing an extension interface. For example, custom rule management, adaptive dynamic data source and so on.

  

Sentinel key features

  

  

Sentinel open source ecosystem

  

Sentinel has been adapted to Servlet, Dubbo, Spring Boot/Spring Cloud, gRPC, etc. users can easily enjoy Sentinel's high available traffic protection capability by introducing corresponding dependency and simple configuration. Sentinel also provides cluster traffic protection capabilities for Service Mesh. In the future sentinel will adapt to more common frameworks.

  

Sentinel is divided into two parts:

  • The core library (Java client) does not rely on any framework / library, and can run in all Java runtime environments. At the same time, it has better support for Dubbo / Spring Cloud and other frameworks.
  • The Dashboard is developed based on Spring Boot, and can be run directly after packaging, without the need for additional application containers such as Tomcat.

  

Sentinel's history

  

  • In 2012, Sentinel was born with the main function of inlet flow control.
  • From 2013 to 2017, sentinel developed rapidly within Alibaba group, becoming a basic technology module, covering all core scenarios. Sentinel also accumulated a large number of traffic consolidation scenarios and production practices.
  • In 2018, Sentinel was open source and evolving.
  • In 2019, Sentinel continues to explore and launch in the direction of multilingual expansion C + + native At the same time, it also launched Envoy cluster flow control support To solve the problem of multi language flow restriction under the Service Mesh architecture.
  • 2020, launch Sentinel Go version , continue to evolve towards cloud native.

  

Sentinel core

  

Sentinel can be used in two parts:

  • Core library (Java client): it does not rely on any framework / library, and can run in the runtime environment of Java 7 and above. At the same time, it has better support for Dubbo / Spring Cloud and other frameworks (see) Mainstream framework adaptation).
  • Dashboard: the console is mainly responsible for managing push rules, monitoring, cluster current limiting distribution management, machine discovery, etc.

  

Sentinel console

  

Sentinel provides a lightweight open source console that provides machine discovery and health management, monitoring (stand-alone and cluster), rule management and push.

Official documents: https://github.com/alibaba/Sentinel/wiki/ Console

  

Get console

  

You can release page Download the latest version of the console jar package.

You can also build the Sentinel console from the latest version of the source code:

  • download Console engineering
  • Use the following command to package the code into a fat jar: mvn clean package

  

start default console

  

The startup command is as follows. The latest version 1.7.2 is used in this article:

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.2.jar

Note: JDK version 1.8 and above is required to start Sentinel console.

Among them- Dserver.port=8080 Specifies that the Sentinel console port is 8080.

Since Sentinel 1.6.0, the Sentinel console has introduced basic login functions. The default user name and password are Sentinel. You can refer to Authentication module document Configure the user name and password.

Note: if your application is a Spring Boot or Spring Cloud application, you can specify the configuration through the Spring configuration file. For details, please refer to Spring Cloud Alibaba Sentinel documentation.

  

To facilitate startup, you can write a startup script run.bat :

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.2.jar
pause

  

visit

  

Visit: http://localhost:8080/

  

Enter the default user name and password sentinel and click login. The console installation is now complete.

  

Environmental preparation

  

Sentinel demo aggregation project. SpringBoot 2.3.0.RELEASE,Spring Cloud Hoxton.SR4 .

  • Nacos registry
  • Product service: commodity service, providing / product/{id} interface
  • Order service rest: order service. Based on the Ribbon, call commodity service through RestTemplate
  • Order server Feign: order service, based on Feign to call commodity service through declarative service

  

  

Client Access Console

  

After the console is started, the client needs to access the console as follows:

  • Add dependency
  • Define resources
  • Define rules

  

Define the resources that may need to be protected before configuring the rules. It can also be understood that as long as we have resources, we can flexibly define various flow control rules at any time. When coding, you only need to consider whether the code needs to be protected, and if so, define it as a resource.

  

Since our project is a Spring Cloud project, we can learn with the help of official documents.

Spring official website documents: https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html

Github document: https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel

  

Add dependency

  

The parent project needs to add the following dependencies:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.1.0.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

The following dependencies need to be added to the subproject:

<!-- spring cloud alibaba sentinel rely on -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

  

configuration file

  

The client needs to start the Transport module to communicate with the Sentinel console.

Order service rest application.yml

spring:
  cloud:
    # Configure Sentinel
    sentinel:
      transport:
        port: 8719
        dashboard: localhost:8080

Here spring.cloud.sentinel.transport.port Port configuration will start an Http Server on the corresponding machine of the application, which will interact with Sentinel console. For example, the Sentinel console adds a current restriction rule, which will push the rule data to the Http Server to receive, and the Http Server will register the rule in Sentinel.

  

Initialize client

  

To ensure that the client has access, Sentinel will initialize when the client first calls and start sending heartbeat packets to the console.

The simple understanding is: after accessing the client once, Sentinel can complete the client initialization operation and continuously send heartbeat packets to the console.

  

visit

  

Multiple visits: http://localhost:9090/order/1 Then check the real-time monitoring results of the console as follows:

  

Define resources

  

Resources are one of the core concepts in sentinel. The resources we are talking about can be anything, services, methods in services, even a piece of code. The most common resource is the Java method in our code. Sentinel provides @ SentinelResource annotation to define resources, and AspectJ extension to automatically define resources, handle BlockException, etc.

As long as the code defined through Sentinel API is a resource, it can be protected by Sentinel. In most cases, you can use method signatures, URL s, or even service names as resource names to identify resources.

Official documents: https://github.com/alibaba/Sentinel/wiki/ How to define resources

  

Annotation support

  

Official documents: https://github.com/alibaba/Sentinel/wiki/ Annotation support

  OrderServiceImpl.java

package com.example.service.impl;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.example.pojo.Order;
import com.example.service.OrderService;
import com.example.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private ProductService productService;

    /**
     * Query order according to primary key and order number
     *
     * @param id
     * @param orderNo
     * @return
     */
    @Override
    @SentinelResource(value = "selectOrderByIdAndOrderNo",
            blockHandler = "selectOrderByIdAndOrderNoBlockHandler",
            fallback = "selectOrderByIdAndOrderNoFallback")
    public Order selectOrderByIdAndOrderNo(Integer id, String orderNo) {
        return new Order(id, orderNo, "China", 2666D,
                Arrays.asList(productService.selectProductById(1)));
    }

    // For service flow control processing, one more BlockException is added at the end of the parameter, and the rest is consistent with the original function.
    public Order selectOrderByIdAndOrderNoBlockHandler(Integer id, String orderNo,
                                                       BlockException ex) {
        // Do some log here.
        ex.printStackTrace();
        return new Order(id, "Service flow control processing-Supporting data", "China", 2666D,
                Arrays.asList(productService.selectProductById(1)));
    }

    // The service fuse is degraded. The function signature is the same as the original function or a Throwable type parameter is added
    public Order selectOrderByIdAndOrderNoFallback(Integer id, String orderNo,
                                                   Throwable throwable) {
        System.out.println("order-service Serving selectOrderById Method exception, exception information is as follows:"
                + throwable);
        return new Order(id, "Degradation treatment of service fuse-Supporting data", "China", 2666D,
                Arrays.asList(productService.selectProductById(1)));
    }

}

  

  ProductServiceImpl.java

package com.example.service.impl;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.example.pojo.Product;
import com.example.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

/**
 * Commodity management
 */
@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private RestTemplate restTemplate;

    /**
     * Query goods according to the primary key
     *
     * @param id
     * @return
     */
    @SentinelResource(value = "selectProductById",
            blockHandler = "selectProductByIdBlockHandler", fallback = "selectProductByIdFallback")
    @Override
    public Product selectProductById(Integer id) {
        return restTemplate.getForObject("http://product-service/product/" + id, Product.class);
    }

    // For service flow control processing, one more BlockException is added at the end of the parameter, and the rest is consistent with the original function.
    public Product selectProductByIdBlockHandler(Integer id, BlockException ex) {
        // Do some log here.
        ex.printStackTrace();
        return new Product(id, "Service flow control processing-Supporting data", 1, 2666D);
    }

    // The service fuse is degraded. The function signature is the same as the original function or a Throwable type parameter is added
    public Product selectProductByIdFallback(Integer id, Throwable throwable) {
        System.out.println("product-service Serving selectProductById Method exception, exception information is as follows:"
                + throwable);
        return new Product(id, "Degradation treatment of service fuse-Supporting data", 1, 2666D);
    }

}

Note: the private method is not supported in the annotation mode.

  

@SentinelResource is used to define resources and provide optional exception handling and fallback configuration items. @The SentinelResource annotation contains the following properties:

  • value: resource name, required (cannot be empty)
  • entryType: entry type, optional (default is EntryType.OUT )
  • blockHandler / blockHandlerClass: blockHandler corresponds to the name of the function handling BlockException, optional. 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 needs to be added at the end. The type is BlockException. The blockHandler function needs to be in the same Class as the original method by default. If you want to use functions of other classes, you can specify blockHandlerClass as the Class object of the corresponding Class. Note that the corresponding function must be a static function, otherwise it cannot be resolved.
  • Fallback: the name of the fallback function, optional, used to provide fallback processing logic when an exception is thrown. The fallback function can handle all types of exceptions except those excluded in exceptionsToIgnore. Fallback function signature and location requirements:
    • The return value type must be the same as that of the original function;
    • The method parameter list needs to be consistent with the original function, or an additional Throwable type parameter can be used to receive the corresponding exception.
    • The fallback function needs to be in the same Class as the original method by default. If you want to use functions of other classes, you can specify fallbackClass as the Class object of the corresponding Class. Note that the corresponding function must be a static function, otherwise it cannot be resolved.
  • Default fallback (since 1.6.0): the name of the default fallback function, optional, usually used for general fallback logic (that is, it can be used for many services or methods). The default fallback function can handle all types of exceptions except those excluded in exceptionsToIgnore. If both fallback and defaultFallback are configured, only fallback will take effect. Signature requirements for defaultFallback function:
    • The return value type must be the same as that of the original function;
    • The method parameter list needs to be empty, or an additional Throwable type parameter can be used to receive the corresponding exception.
    • The defaultFallback function needs to be in the same Class as the original method by default. If you want to use functions of other classes, you can specify fallbackClass as the Class object of the corresponding Class. Note that the corresponding function must be a static function, otherwise it cannot be resolved.
  • Exceptions to ignore (since 1.6.0): used to specify which exceptions are excluded, which will not be included in the exception statistics, nor enter the fallback logic, but will be thrown as is.

Note: before 1.6.0, the fallback function only deals with the DegradeException, not the business exception.

In particular, if both blockHandler and fallback are configured, only the blockHandler processing logic will be entered when the BlockException is thrown due to current limiting degradation. If blockHandler, fallback and defaultFallback are not configured, BlockException will be thrown directly when it is degraded by current restriction (if throws BlockException is not defined by the method itself, it will be wrapped by JVM with a layer of undeclared throwableexception).

Starting from version 1.4.0, the annotation method defines that resources support automatic statistics of business exceptions without manual call Tracer.trace(ex) to record business exceptions. Sentinel before 1.4.0 needs to be called by itself Tracer.trace(ex) to record business exceptions.

  

Define rules

  

All sentinel rules can be queried and modified dynamically in memory state, and will take effect immediately after modification. Sentinel also provides relevant API s for you to customize your rules and policies.

Sentinel supports the following rules: flow control rules, fuse degradation rules, hotspot parameter rules, system protection rules and source access control rules.

Official documents: https://github.com/alibaba/Sentinel/wiki/ How to use the types of rules

  

Flow control rules

  

Add flow control rule

  

Select the cluster point link to find the defined resource selectProductById and click the corresponding rule button to set it.

  

For example, we set a flow control rule to define the QPS of resource access as 1 (the number of queries that can be processed per second).

  

test

  

Quick refresh page multiple visits: http://localhost:9090/order/idAndOrderNo?id=1&orderNo=order-001 The results are as follows:

  

Fusing degradation rules

  

Error impersonating service

  

Modify the core code in the order service rest project to simulate service errors.

package com.example.service.impl;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.example.pojo.Product;
import com.example.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

/**
 * Commodity management
 */
@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private RestTemplate restTemplate;

    /**
     * Query goods according to the primary key
     *
     * @param id
     * @return
     */
    @SentinelResource(value = "selectProductById",
            blockHandler = "selectProductByIdBlockHandler", fallback = "selectProductByIdFallback")
    @Override
    public Product selectProductById(Integer id, String productName) {
        // Querying commodity information with primary key 1 in simulation will result in exceptions
        if (1 == id)
            throw new RuntimeException("Exception caused by querying the commodity information with primary key 1");
        return restTemplate.getForObject("http://product-service/product/" + id, Product.class);
    }

    // For service flow control processing, one more BlockException is added at the end of the parameter, and the rest is consistent with the original function.
    public Product selectProductByIdBlockHandler(Integer id, BlockException ex) {
        // Do some log here.
        ex.printStackTrace();
        return new Product(id, "Service flow control processing-Supporting data", 1, 2666D);
    }

    // The service fuse is degraded. The function signature is the same as the original function or a Throwable type parameter is added
    public Product selectProductByIdFallback(Integer id, Throwable throwable) {
        System.out.println("product-service Serving selectProductById Method exception, exception information is as follows:"
                + throwable);
        return new Product(id, "Degradation treatment of service fuse-Supporting data", 1, 2666D);
    }

}

  

Add fuse degradation rule

  

The fuse degradation rule supports three ways: corresponding time, abnormal proportion and abnormal number.

  

test

  

Visit: http://localhost:9090/order/idAndOrderNo?id=1&orderNo=order-001 The results are as follows:

  

Hot spot parameter rule

  

Hotspot parameter rule is a more fine-grained flow control rule, which allows rules to be specific to parameters. For example, the selectOrderByIdAndOrderNo method has two parameters. We restrict the flow of the first parameter and do not restrict the flow of the second parameter.

  

Add hotspot parameter rule

  

Select the cluster link to find the defined resource selectOrderByIdAndOrderNo and click the corresponding rule button to set it.

  

Set the hotspot parameter rule to define the QPS of the first parameter of the resource as 1 (the number of queries that can be processed per second).

  

test

  

When two parameters are used for access, it will be found that only the first parameter is limited.

Quick refresh page multiple visits: http://localhost:9090/order/idAndOrderNo?id=1 Restricted current.

Quick refresh page multiple visits: http://localhost:9090/order/idAndOrderNo?orderNo=order-001 Normal access.

  

Authorization rules

  

In many cases, we need to judge whether the request is allowed or not according to the call source. At this time, we can use Sentinel's source access control function. Source access control restricts whether a resource passes according to its request origin.

Sentinel provides the RequestOriginParser interface to handle the source. Once the interface resource protected by sentinel is accessed, sentinel will call the implementation class of RequestOriginParser to resolve the access source.

  

Custom source processing rules

  

package com.example.sentinel;

import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * Custom source processing rules
 */
@Component
public class MyRequestOriginParser implements RequestOriginParser {

    @Override
    public String parseOrigin(HttpServletRequest request) {
        return request.getParameter("userName");
    }

}

  

New authorization rule

  

The configuration in the figure below means that only users with userName=zhangsan cannot access the resource selectOrderByIdAndOrderNo (blacklist)

  

test

  

Quick refresh page multiple visits: http://localhost:9090/order/idAndOrderNo?id=1&userName=zhangsan Restricted current.

Quick refresh page multiple visits: http://localhost:9090/order/idAndOrderNo?id=1&userName=lisi Normal access.

  

System protection rules

  

The system protection rule is to control the application level entrance flow, monitor the application data from the five dimensions of the overall LOAD, RT, thread number, entrance QPS and CPU utilization of a single machine, so that the system can run at the maximum throughput and ensure the overall stability of the system.

The system protection rules apply the overall dimension, not the resource dimension, and only take effect on the entry traffic (the traffic entering the application).

  • Load (only valid for Linux / Unix like machines): system protection will be triggered when the system load exceeds the threshold value and the current number of concurrent threads exceeds the system capacity. The system capacity is calculated by maxQps * minRt of the system. The setting reference value is generally CPU cores * 2.5.
  • RT: when the average RT of all the inlet flows on a single machine reaches the threshold value, the system protection will be triggered, in milliseconds.
  • Number of threads: when the number of concurrent threads of all the entrance traffic on a single machine reaches the threshold, system protection is triggered.
  • Inlet QPS: when the QPS of all inlet flows on a single machine reaches the threshold value, the system protection will be triggered.
  • CPU utilization: when the CPU utilization of all the inlet traffic on a single machine reaches the threshold value, system protection will be triggered.

  

Dynamic rule extension

  

Official documents:

  

The datasource property of the TreeMap type is provided inside SentinelProperties to configure the data source information. support:

  • File configuration rules
  • Nacos configuration rules
  • ZooKeeper configuration rules
  • Apollo configuration rules
  • Redis configuration rules

  

File configuration rules

  

Sentinel supports loading rule configuration through local file, which is used as follows (current restriction rule as demonstration):

spring:
  cloud:
    # Configure Sentinel
    sentinel:
      datasource:
        ds1:
          file:
            file: classpath:flowRule.json
            data-type: json
            rule-type: flow

  

   flowRule.json corresponding com.alibaba.csp.sentinel.slots.block.RuleConstant properties.

[
  {
    "resource": "selectProductList",
    "count": 1,
    "grade": 1,
    "limitApp": "default",
    "strategy": 0,
    "controlBehavior": 0
  }
]

  

Important attributes:

Field explain Default
resource Resource name is the object of the current restriction rule
count Current limiting threshold
grade Current limit threshold type, QPS mode (1) or concurrent threads mode (0) QPS mode
limitApp Call source for flow control default, indicating that the call source is not distinguished
strategy Call relationship current restriction policy: direct, link, Association According to the resource itself (direct)
controlBehavior Flow control effect (direct reject / queuing / slow start mode), flow restriction by call relationship is not supported Direct rejection
clusterMode Cluster current limiting or not no

  

After accessing the client, refresh the console and view the flow control rules as follows:

  

RestTemplate support

  

Spring Cloud Alibaba Sentinel supports service protection for services called by RestTemplate. The @ SentinelRestTemplate annotation needs to be added when constructing the RestTemplate Bean.

  

Startup class

  

  OrderServiceRestApplication.java

package com.example;

import com.alibaba.cloud.sentinel.annotation.SentinelRestTemplate;
import com.example.exception.ExceptionUtil;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class OrderServiceRestApplication {

    @Bean
    @LoadBalanced
    @SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class,
            fallback = "fallback", fallbackClass = ExceptionUtil.class)
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

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

}

  

Service fuse processing

  

   ExceptionUtil.java Static methods must be used.

package com.example.exception;

import com.alibaba.cloud.sentinel.rest.SentinelClientHttpResponse;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSON;
import com.example.pojo.Product;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpResponse;

public class ExceptionUtil {

    // Service flow control processing
    public static ClientHttpResponse handleException(HttpRequest request,
                                                     byte[] body,
                                                     ClientHttpRequestExecution execution,
                                                     BlockException exception) {
        exception.printStackTrace();
        return new SentinelClientHttpResponse(
                JSON.toJSONString(new Product(1, "Service flow control processing-Supporting data", 1, 2666D)));
    }

    // Degradation treatment of service fuse
    public static ClientHttpResponse fallback(HttpRequest request,
                                                    byte[] body,
                                                    ClientHttpRequestExecution execution,
                                                    BlockException exception) {
        exception.printStackTrace();
        return new SentinelClientHttpResponse(
                JSON.toJSONString(new Product(1, "Degradation treatment of service fuse-Supporting data", 1, 2666D)));
    }

}

  

visit

  

The console sets the flow control rules to define the QPS of resource access as 1 (the number of queries that can be processed per second).

Quick refresh page multiple visits: http://localhost:9090/order/1 The results are as follows:

  

OpenFeign support

  

Add dependency

  

<!-- spring cloud alibaba sentinel rely on -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- spring cloud openfeign rely on -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

  

Start Sentinel

  

server:
  port: 9091 # port

spring:
  application:
    name: order-service-feign # apply name
  cloud:
    # Configure Nacos registry
    nacos:
      discovery:
        enabled: true # If you do not want to use Nacos for service registration and discovery, set it to false
        server-addr: 127.0.0.1:8848 # Nacos server address
    # Configure Sentinel
    sentinel:
      transport:
        port: 8719
        dashboard: localhost:8080

# feign enable sentinel support
feign:
  sentinel:
    enabled: true

  

Fusing degradation

  

  ProductServiceFallback.java

package com.example.fallback;

import com.example.pojo.Product;
import com.example.service.ProductService;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * Service fuse degradation processing can catch exceptions
 */
@Slf4j
@Component
public class ProductServiceFallbackFactory implements FallbackFactory<ProductService> {

    @Override
    public ProductService create(Throwable throwable) {
        return new ProductService() {
            @Override
            public Product selectProductById(Integer id) {
                // Get the log and handle it in the methods that need to catch exceptions
                log.error("product-service Serving selectProductById Method exception, exception information is as follows:"
                        + throwable);
                return new Product(id, "Supporting data", 1, 2666D);
            }
        };
    }

}

  

Consumer services

  

  ProductService.java

package com.example.service;

import com.example.fallback.ProductServiceFallbackFactory;
import com.example.pojo.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

// Declare the service to be called
@FeignClient(value = "product-service", fallbackFactory = ProductServiceFallbackFactory.class)
public interface ProductService {

    /**
     * Query goods according to the primary key
     *
     * @param id
     * @return
     */
    @GetMapping("/product/{id}")
    Product selectProductById(@PathVariable("id") Integer id);

}

  

  OrderServiceImpl.java

package com.example.service.impl;

import com.example.pojo.Order;
import com.example.service.OrderService;
import com.example.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private ProductService productService;

    /**
     * Query order based on primary key
     *
     * @param id
     * @return
     */
    @Override
    public Order selectOrderById(Integer id) {
        return new Order(id, "order-001", "China", 2666D,
                Arrays.asList(productService.selectProductById(1)));
    }

}

  

Control layer

  

package com.example.controller;

import com.example.pojo.Order;
import com.example.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    /**
     * Query order based on primary key
     *
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public Order selectOrderById(@PathVariable("id") Integer id) {
        return orderService.selectOrderById(id);
    }

}

  

Startup class

  

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

// Open FeignClients annotation
@EnableFeignClients
// Enable @ EnableDiscoveryClient annotation, which will be enabled by default in the current version
//@EnableDiscoveryClient
@SpringBootApplication
public class OrderServiceFeignApplication {

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

}

  

test

  

The console information is as follows:

  

Add traffic control rules to define the QPS of resource access as 1 (the number of queries that can be processed per second).

  

Quick refresh page multiple visits: http://localhost:9091/order/1 The results are as follows:

  

Or close the service provider and access: http://localhost:9091/order/1 The results are as follows:

  

Gateway support

  

Sentinel supports current limiting for mainstream API gateways such as Spring Cloud Gateway and Netflix Zuul.

Official documents:

  

Create project

  

Create the gateway server sentinel project.

  

Add dependency

  

Use alone to add sentinel spring cloud gateway adapter dependency.

If you want to use it with Sentinel Starter, you need to add the spring cloud Alibaba sentinel gateway dependency to make the Spring Cloud Gateway automatic configuration class in the spring cloud Alibaba sentinel gateway module take effect.

At the same time, please spring.cloud.sentinel.filter.enabled The configuration item is set to false (if you see the URL resource on the gateway flow control console, this configuration item is not set to false).

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <!-- Inherit parent dependency -->
    <parent>
        <artifactId>gateway-demo</artifactId>
        <groupId>com.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gateway-server-sentinel</artifactId>

    <!-- Project dependency -->
    <dependencies>
        <!-- spring cloud gateway rely on -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!-- spring cloud alibaba nacos discovery rely on -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- Use alone -->
        <!-- sentinel gateway adapter rely on -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
        </dependency>
        <!-- and Sentinel Starter Use together -->
        <!--
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>
        -->
    </dependencies>

</project>

  

configuration file

  

server:
  port: 9001 # port

spring:
  application:
    name: gateway-server-sentinel # apply name
  cloud:
    sentinel:
      filter:
        enabled: false
    gateway:
      discovery:
        locator:
          # Whether to combine with service discovery component and forward to specific service instance through serviceId.
          enabled: true                  # Enable routing rules based on service discovery
          lower-case-service-id: true    # Convert service name to lowercase
      # Routing rules
      routes:
        - id: order-service           # Route ID, unique
          uri: lb://Order service ා target URI, lb: / / get the service request address from the registry according to the service name
          predicates:                 # Assertion (judgment condition)
            # Match the request of the corresponding URI, and append the matched request after the target URI
            - Path=/order/**

  

Current limit rule configuration class

  

When using, just inject the corresponding SentinelGatewayFilter instance and SentinelGatewayBlockExceptionHandler instance.

  GatewayConfiguration.java

package com.example.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;

import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Current limit rule configuration class
 */
@Configuration
public class GatewayConfiguration {

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    /**
     * constructor 
     *
     * @param viewResolversProvider
     * @param serverCodecConfigurer
     */
    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /**
     * Current limiting exception processor
     *
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    /**
     * Restriction filter
     *
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /**
     * Spring Execute this method when the container is initialized
     */
    @PostConstruct
    public void doInit() {
        // Load gateway current restriction rules
        initGatewayRules();
    }

    /**
     * Gateway current restriction rules
     */
    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        /*
            resource: Resource name, which can be the route name in the gateway or the user-defined API group name
            count: Current limiting threshold
            intervalSec: Statistics time window, unit is second, default is 1 second
         */
        rules.add(new GatewayFlowRule("order-service")
                .setCount(3) // Current limiting threshold
                .setIntervalSec(60)); // Statistics time window, unit is second, default is 1 second
        // Load gateway current restriction rules
        GatewayRuleManager.loadRules(rules);
    }

}

  

Startup class

  

  GatewayServerSentinelApplication.java

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// Enable the EurekaClient annotation. If the current version is configured with Eureka registry, the annotation will be enabled by default
//@EnableEurekaClient
@SpringBootApplication
public class GatewayServerSentinelApplication {

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

}

  

visit

  

Multiple visits: http://localhost:9001/order/1 The results are as follows:

The default implementation of the interface BlockRequestHandler is DefaultBlockRequestHandler. When the current restriction is triggered, the default error message will be returned: Blocked by Sentinel: FlowException. We can customize exception prompt information through GatewayCallbackManager.

  

Custom exception prompt

  

The setBlockHandler registration function of GatewayCallbackManager is used to implement the custom logic and handle the flow limited requests.

package com.example.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * Current limit rule configuration class
 */
@Configuration
public class GatewayConfiguration {

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    /**
     * constructor 
     *
     * @param viewResolversProvider
     * @param serverCodecConfigurer
     */
    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /**
     * Current limiting exception processor
     *
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    /**
     * Restriction filter
     *
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /**
     * Spring Execute this method when the container is initialized
     */
    @PostConstruct
    public void doInit() {
        // Load gateway current restriction rules
        initGatewayRules();
        // Load custom current limit exception handler
        initBlockHandler();
    }

    /**
     * Gateway current restriction rules
     */
    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        /*
            resource: Resource name, which can be the route name in the gateway or the user-defined API group name
            count: Current limiting threshold
            intervalSec: Statistics time window, unit is second, default is 1 second
         */
        rules.add(new GatewayFlowRule("order-service")
                .setCount(3) // Current limiting threshold
                .setIntervalSec(60)); // Statistics time window, unit is second, default is 1 second
        // Load gateway current restriction rules
        GatewayRuleManager.loadRules(rules);
    }

    /**
     * Custom current limiting exception handler
     */
    private void initBlockHandler() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map<String, String> result = new HashMap<>();
                result.put("code", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
                result.put("message", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
                result.put("route", "order-service");
                return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(result));
            }
        };

        // Load custom current limit exception handler
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }

}

  

visit

  

Multiple visits: http://localhost:9001/order/1 The results are as follows:

  

Group current limiting

  

package com.example.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * Current limit rule configuration class
 */
@Configuration
public class GatewayConfiguration {

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    /**
     * constructor 
     *
     * @param viewResolversProvider
     * @param serverCodecConfigurer
     */
    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /**
     * Current limiting exception processor
     *
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    /**
     * Current limiting filter
     *
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /**
     * Spring Execute this method when the container is initialized
     */
    @PostConstruct
    public void doInit() {
        // Load gateway current restriction rules
        initGatewayRules();
        // Load custom current limit exception handler
        initBlockHandler();
    }

    /**
     * Gateway current restriction rules
     */
    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        /*
            resource: Resource name, which can be the route name in the gateway or the user-defined API group name
            count: Current limiting threshold
            intervalSec: Statistics time window, unit is second, default is 1 second
         */
        // rules.add(new GatewayFlowRule("order-service")
        //         . setCount(3) / / current limiting threshold
        //         . setIntervalSec(60)); / / statistics time window, unit is second, default is 1 second
        // --------------------Current limiting group ------------ start----------
        rules.add(new GatewayFlowRule("product-api")
                .setCount(3) // Current limiting threshold
                .setIntervalSec(60)); // Statistics time window, unit is second, default is 1 second
        rules.add(new GatewayFlowRule("order-api")
                .setCount(5) // Current limiting threshold
                .setIntervalSec(60)); // Statistics time window, unit is second, default is 1 second
        // --------------------Current limiting group -------- end-----------
        // Load gateway current restriction rules
        GatewayRuleManager.loadRules(rules);
        // Load current limiting group
        initCustomizedApis();
    }

    /**
     * Custom current limiting exception handler
     */
    private void initBlockHandler() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map<String, String> result = new HashMap<>();
                result.put("code", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
                result.put("message", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
                result.put("route", "order-service");
                return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(result));
            }
        };

        // Load custom current limit exception handler
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }

    /**
     * Current limiting grouping
     */
    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        // Product API Group
        ApiDefinition api1 = new ApiDefinition("product-api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    // All requests matching / product service / product and its child paths
                    add(new ApiPathPredicateItem().setPattern("/product-service/product/**")
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});

        // Order API Group
        ApiDefinition api2 = new ApiDefinition("order-api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    // Match only / order service / order / index
                    add(new ApiPathPredicateItem().setPattern("/order-service/order/index"));
                }});
        definitions.add(api1);
        definitions.add(api2);
        // Load current limiting group
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

}

  

visit

  

Visit: http://localhost:9001/product-service/product/1 Trigger current limiting

Visit: http://localhost:9001/order-service/order/index Trigger current limiting

Visit: http://localhost:9001/order-service/order/1 Current limiting will not be triggered

  

At this point, Sentinel service Sentinel knowledge point is over.

This paper adopts Intellectual sharing "signature - non-commercial use - no deduction 4.0 international" License Agreement.

You can go through classification See more about Spring Cloud The article.

  

🤗 Your comments and forwarding are my biggest support.

📢 Scan the code and follow Mr. halloward's "document + video". Each article is provided with a special video explanation, which makes learning easier~

Tags: Programming Spring Java github REST

Posted on Thu, 11 Jun 2020 22:32:15 -0400 by thelinx