Alibaba Sentinel is a high performance and lightweight flow control, circuit breaker degradation solution. Is a highly available flow control component for distributed services architecture.

The Sentinel’s official website: https://sentinelguard.io/zh-cn/

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

What is the Sentinel

With the popularity of microservices, stability between services becomes increasingly important. Sentinel mainly takes traffic as the entry point to ensure the stability of micro-service from multiple dimensions such as flow control, fusing downgrading and system adaptive protection.

Sentinel has the following characteristics:

  • “Rich application scenarios” : Sentinel has undertaken the core scenarios of Alibaba’s double Eleven traffic drive in the past 10 years, such as SEC killing (i.e. burst traffic control within the range of system capacity), message peaking and valley filling, cluster flow control, real-time fusing of unavailable downstream applications, etc.
  • “Complete real-time monitoring” : Sentinel also provides real-time monitoring capabilities. From the console, you can see a summary of the performance of a single machine-by-second data, or even a cluster of less than 500 machines, for accessing the application.
  • “Extensive Open Source ecosystem” : Sentinel provides out-of-the-box integration modules with other open source frameworks/libraries, such as Spring Cloud, Dubbo and gRPC. You can quickly access Sentinel by introducing the appropriate dependencies and simple configuration.
  • “Perfect SPI extension Point” : Sentinel provides an easy-to-use, perfect SPI extension interface. You can quickly customize the logic by implementing an extension interface. For example, customize rule management and adapt dynamic data sources.

Main features of Sentinel


Sentinel Open Source ecology


Sentinel has been adapted for Servlet, Dubbo, Spring Boot/Spring Cloud, gRPC, etc. Users can easily enjoy Sentinel’s high availability traffic protection capability by introducing corresponding dependencies and simple configuration. Sentinel also provides cluster traffic protection capabilities for Service Mesh. More commonly used frameworks will be adapted for Sentinel in the future.

Sentinel is divided into two parts:

  • The core library (Java client) is independent of any framework/library, can run in all Java runtime environments, and has good support for frameworks such as Dubbo/Spring Cloud.
  • The Console (Dashboard) is based on Spring Boot and can be packaged to run directly without the need for additional application containers such as Tomcat.

The history of the Sentinel

  • Sentinel was born in 2012 and its main function is inlet flow control.
  • From 2013 to 2017, Sentinel developed rapidly within Alibaba Group and became a basic technology module, covering all core scenarios. Sentinel has thus accumulated a large number of traffic aggregation scenarios and production practices.
  • Sentinel became open source in 2018 and continues to evolve.
  • In 2019, Sentinel continued to explore multilingual extensions with the release of a C++ native version and Envoy cluster traffic control support for Service Mesh scenarios to address multilingual traffic limiting issues under the Service Mesh architecture.
  • In 2020, Sentinel Go was released, continuing its evolution toward cloud native.

Sentinel core

The use of Sentinel can be divided into two parts:

  • Core Library (Java client) : A runtime environment that is independent of any framework/library, can run on Java 7 and above, and has good support for frameworks such as Dubbo/Spring Cloud (see mainstream framework adaptation).
  • Console (Dashboard) : The console is mainly responsible for managing push rules, monitoring, cluster traffic limiting allocation management, and machine discovery.

Sentinel console

Sentinel provides a lightweight open source console that provides machine discovery as well as health management, monitoring (single and cluster), rule management and push capabilities.

Official website: github.com/alibaba/Sen…

Access console

You can download the latest version of the console JAR package from the Release page.

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

  • Download Console Engineering
  • Package the code into a fat JAR using the following command:mvn clean package

Start console

The startup command is as follows. This document uses the latest version 1.7.2:

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

Note: JDK version 1.8 or later is required to start the Sentinel console.

-dserver. port=8080 specifies the Sentinel console port as 8080.

Starting with Sentinel 1.6.0, the Sentinel console introduced a basic “login” feature, with the default user name and password being Sentinel. For details about how to configure the user name and password, see the authentication module documentation.

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

To facilitate startup, we 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. Jarpause
Copy the code

access

Visit: http://localhost:8080/


Enter the default username and password sentinel and click login. The console installation is complete.


Environment to prepare

Sentinel-demo polymerization project. RELEASE, Spring Cloud hoxton.sr4

  • Nacos Registry
  • product-service: Goods and services, provided/product/{id}interface
  • order-service-rest: Order service, based onRibbonthroughRestTemplateInvoking goods and services
  • order-server-feign: Order service, based onFeignInvoke commodity services through declarative services

The client accesses the console

After the console is started, perform the following steps to connect the client to the console:

  • Add the dependent
  • Define the resources
  • Define the rules

Define resources that may need to be protected before configuring rules. It can also be understood that as long as we have the resources, we have the flexibility to 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 documentation.

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

Making documents: https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel

Add the dependent

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> Copy the code

Subprojects need to add the following dependencies:

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

The configuration file

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

The order – service – the rest of the application. Yml

spring:
  cloud:
    # configuration Sentinel
    sentinel:
 transport:
 port: 8719  dashboard: localhost:8080 Copy the code

The spring here. Cloud. Sentinel. Transport. The port port configuration in the application of equivalent machine start an Http Server, the Server will make interaction with sentinel console. For example, when the Sentinel console adds a limiting rule, it pushes the rule data to the Http Server, which then registers the rule with Sentinel.

Initialize the client

“Ensure client traffic,” Sentinel initializes “the first time the client is called,” and starts sending heartbeat packets to the console.

Sentinel is able to initialize the client and continuously send heartbeat packets to the console by accessing the client once.

access

Repeatedly visit: http://localhost:9090/order/1 and then look at the console real-time monitoring the results are as follows:


Define the resources

Resource is one of the core concepts in Sentinel. When we say resource, it can be anything, a service, a method in a service, even a piece of code. The most common resources are Java methods in our code. Sentinel provides the @SentinelResource annotation for defining resources and an extension to AspectJ for automatically defining resources, handling blockexceptions, and so on.

Any code defined through the Sentinel API is a resource that can be protected by Sentinel. In most cases, resources can be identified using method signatures, urls, and even service names as resource names.

Official website: github.com/alibaba/Sen…

Annotation support

Official website: github.com/alibaba/Sen…

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 orders based on 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)));  }   // Add a BlockException at the end of the parameter. The rest is the same as 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 - Backing data"."China".2666D,  Arrays.asList(productService.selectProductById(1)));  }   // Add a Throwable parameter to the function signature  public Order selectOrderByIdAndOrderNoFallback(Integer id, String orderNo,  Throwable throwable) {  System.out.println("The selectOrderById method of the order-service service is abnormal. The exception information is as follows:  + throwable);  return new Order(id, "Service fuse Downgrade Processing - Bottom data"."China".2666D,  Arrays.asList(productService.selectProductById(1)));  }  } Copy the code

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 by 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);  }   // Add a BlockException at the end of the parameter. The rest is the same as the original function.  public Product selectProductByIdBlockHandler(Integer id, BlockException ex) {  // Do some log here.  ex.printStackTrace();  return new Product(id, "Service Flow Control Processing - Backing data".1.2666D);  }   // Add a Throwable parameter to the function signature  public Product selectProductByIdFallback(Integer id, Throwable throwable) {  System.out.println("The selectProductById method of the product-service service is abnormal. The exception information is as follows:  + throwable);  return new Product(id, "Service fuse Downgrade Processing - Bottom data".1.2666D);  }  } Copy the code

Note: The annotation method does not support the private method.

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

  • 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, Are current limiting the drop will be BlockException “direct selling” (if the method itself undefined throws BlockException packing layer would be JVM UndeclaredThrowableException).

Starting from version 1.4.0, annotated resources support automatic statistics of service exceptions. You do not need to manually call tracer.trace (ex) to record service exceptions. Sentinel versions prior to 1.4.0 required a call to tracer.trace (ex) to log service exceptions.

Define the rules

All Sentinel rules can be “dynamically queried and modified in memory with immediate effect”. Sentinel also provides apis that allow you to customize your own rules policies.

Sentinel supports the following rules: Traffic Control Rules, Fuse degrade rules, Hotspot parameter rules, System protection rules, and Source Access Control rules.

Official website: github.com/alibaba/Sen…

Flow control rule

Add a traffic control rule

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


For example, let’s set a flow control rule that defines the QPS of resource access to 1 (the number of queries that can be processed per second).



test

Fast refresh the page several times visit: http://localhost:9090/order/idAndOrderNo? Id =1&orderNo=order-001


Fuse downgrading rules

Mock service error

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 by primary key *  * @param id  * @return * /  @SentinelResource(value = "selectProductById". blockHandler = "selectProductByIdBlockHandler", fallback = "selectProductByIdFallback")  @Override  public Product selectProductById(Integer id, String productName) {  // An exception will occur when the primary key is 1  if (1 == id)  throw new RuntimeException("Error caused by querying commodity information with primary key 1");  return restTemplate.getForObject("http://product-service/product/" + id, Product.class);  }   // Add a BlockException at the end of the parameter. The rest is the same as the original function.  public Product selectProductByIdBlockHandler(Integer id, BlockException ex) {  // Do some log here.  ex.printStackTrace();  return new Product(id, "Service Flow Control Processing - Backing data".1.2666D);  }   // Add a Throwable parameter to the function signature  public Product selectProductByIdFallback(Integer id, Throwable throwable) {  System.out.println("The selectProductById method of the product-service service is abnormal. The exception information is as follows:  + throwable);  return new Product(id, "Service fuse Downgrade Processing - Bottom data".1.2666D);  }  } Copy the code

Added fuse downgrading rule

There are three types of fusible downgrading rules: corresponding time, abnormal proportion, and abnormal number.


test

Visit: http://localhost:9090/order/idAndOrderNo? Id =1&orderNo=order-001


Hotspot parameter rule

The hot parameter rule is a more fine-grained flow control rule that allows rules to be specific to parameters. SelectOrderByIdAndOrderNo method has two parameters, for example, our current limit of the first parameter to the second parameter unlimited flow.

Add a hotspot parameter rule

Select the link to find clusters defined selectOrderByIdAndOrderNo resources and click on the button to set the corresponding rules.


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



test

Access the two parameters separately, and you will find that only the first parameter is limited.

Fast refresh the page several times visit: http://localhost:9090/order/idAndOrderNo? Id =1 Traffic is limited.


Fast refresh the page several times visit: http://localhost:9090/order/idAndOrderNo? OrderNo =order-001 Normal access.


Authorization rules

In many cases, we need to determine whether a request is allowed based on the source of the call. In this case, we can use Sentinel’s source access control function. Source access control Restricts access to resources based on their origin.

Sentinel provides a RequestOriginParser interface to handle sources. Once an interface resource protected by Sentinel is accessed, Sentinel calls the Implementation class of RequestOriginParser to resolve the source of the access.

Custom source handling 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 handling rules* / @Component public class MyRequestOriginParser implements RequestOriginParser {   @Override  public String parseOrigin(HttpServletRequest request) {  return request.getParameter("userName");  }  } Copy the code

Adding authorization Rules

Below allocation means selectOrderByIdAndOrderNo only userName = zhangsan users cannot access (blacklist)


test

Fast refresh the page several times visit: http://localhost:9090/order/idAndOrderNo? Id =1&userName=zhangsan Traffic is restricted.


Fast refresh the page several times visit: http://localhost:9090/order/idAndOrderNo? Id =1&userName=lisi Normal access.


System Protection Rules

System protection rules control the incoming flow at the application level, and monitor application data from the total LOAD, RT, threads, incoming QPS and CPU usage of a single machine, so that the system can run at the maximum throughput and ensure the overall stability of the system.

System protection rules are application-wide, not resource-wide, and only apply to incoming traffic (traffic entering the application).

  • Load (only for Linux or UNIX-like machines) : When the Load of the system exceeds the threshold and the number of concurrent threads exceeds the system capacity, system protection is triggered. The system capacity is calculated by the system maxQps * minRt. The reference values are typically CPU cores * 2.5.
  • RT: System protection is triggered when the average RT of all incoming traffic on a single machine reaches a threshold, in milliseconds.
  • Number of threads: System protection is triggered when the number of concurrent threads for all incoming traffic on a single machine reaches a threshold.
  • Inlet QPS: System protection is triggered when the QPS of all inlet flows on a single machine reaches the threshold.
  • CPU usage: When the CPU usage of all incoming traffic on a single machine reaches the threshold, system protection is triggered.

Dynamic rule extension

Official website documents:

  • Github.com/alibaba/spr…
  • Github.com/alibaba/Sen…

Internally, the treemap-type datasource property is provided to SentinelProperties to configure datasource information. Support:

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

Sentinel supports the configuration of load rules from local files, which can be used as follows (traffic limiting rules are used as a demonstration) :

spring:
  cloud:
    # configuration Sentinel
    sentinel:
 datasource:
 ds1:  file:  file: classpath:flowRule.json  data-type: json  rule-type: flow Copy the code

FlowRule. Json corresponding com. Alibaba. CSP. Sentinel. Slots. Block. RuleConstant each attribute.

[
  {
    "resource": "selectProductList".    "count": 1.    "grade": 1. "limitApp": "default". "strategy": 0. "controlBehavior": 0  } ] Copy the code

Important attributes:

Field instructions The default value
resource The resource name is the object of the traffic limiting rule
count Current limiting threshold
grade Flow limiting threshold type, QPS mode (1) or Number of concurrent threads mode (0) QPS model
limitApp The source of the call for flow control default, indicates that the call source is not distinguished
strategy Call relationship traffic limiting policies: direct, link, and association According to the resource itself (direct)
controlBehavior Flow control effect (direct reject/queue wait/slow start mode). Traffic limiting by call relationship is not supported Direct refused to
clusterMode Whether to restrict traffic in a cluster 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 invoked by RestTemplate. The @SentinelRestTemplate annotation needs to be added when constructing the RestTemplate Bean.

Start the 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(a) {  return new RestTemplate();  }   public static void main(String[] args) {  SpringApplication.run(OrderServiceRestApplication.class, args);  }  } Copy the code

Service fusing processing

Exceptionutil.java must use static methods.

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 traffic 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 - Backing data".1.2666D)));  }   // Service fuse downgrade process  public static ClientHttpResponse fallback(HttpRequest request,  byte[] body,  ClientHttpRequestExecution execution,  BlockException exception) {  exception.printStackTrace();  return new SentinelClientHttpResponse(  JSON.toJSONString(new Product(1."Service fuse Downgrade Processing - Bottom data".1.2666D)));  }  } Copy the code

access

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

The results are as follows: fast refresh the page several times visit: http://localhost:9090/order/1


OpenFeign support

Add the dependent

<! -- Spring Cloud Alibaba Sentinel -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<! -- Spring Cloud OpenFeign --> <dependency>  <groupId>org.springframework.cloud</groupId>  <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> Copy the code

Open the Sentinel

server:
  port: 9091 # port

spring:
 application:
 name: order-service-feign # app name  cloud:  Configure the Nacos registry  nacos:  discovery:  enabled: true If you don't want to use Nacos for service registration and discovery, set it to false  server-addr: 127.0. 01.: 8848 # Nacos server address  # configuration Sentinel  sentinel:  transport:  port: 8719  dashboard: localhost:8080  # feign Enable sentinel support feign:  sentinel:  enabled: true Copy the code

Fusing the drop

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 downgrade 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 process it in the method that needs to catch the exception  log.error("The selectProductById method of the product-service service is abnormal. The exception information is as follows:  + throwable);  return new Product(id, "Bottom data".1.2666D);  }  };  }  } Copy the code

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 invoked @FeignClient(value = "product-service", fallbackFactory = ProductServiceFallbackFactory.class) public interface ProductService {   / * ** Query goods by primary key *  * @param id  * @return * /  @GetMapping("/product/{id}")  Product selectProductById(@PathVariable("id") Integer id);  } Copy the code

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 orders based on primary keys *  * @param id  * @return * /  @Override  public Order selectOrderById(Integer id) {  return new Order(id, "order-001"."China".2666D,  Arrays.asList(productService.selectProductById(1)));  }  } Copy the code

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 orders based on primary keys *  * @param id  * @return * /  @GetMapping("/{id}")  public Order selectOrderById(@PathVariable("id") Integer id) {  return orderService.selectOrderById(id);  }  } Copy the code

Start the class

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
 // Enable the FeignClients annotation @EnableFeignClients // Enable the @enableDiscoveryClient annotation. This annotation is enabled by default in the current version //@EnableDiscoveryClient @SpringBootApplication public class OrderServiceFeignApplication {   public static void main(String[] args) {  SpringApplication.run(OrderServiceFeignApplication.class, args);  }  } Copy the code

test

The console information is as follows:


Add a flow control rule that defines the QPS of resource access to 1 (the number of queries that can be processed per second).


The results are as follows: fast refresh the page several times visit: http://localhost:9091/order/1


Or shut down the service provider, visit: http://localhost:9091/order/1 the results are as follows:


Gateway support

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


Official website documents:

  • https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel
  • Github.com/alibaba/Sen…

Create a project

Create a gateway-server-Sentinel project.

Add the dependent

Use the sentinel-Spring-Cloud-gateway-Adapter dependency alone.

To use it with Sentinel Starter, The spring-cloud-alibaba-sentinel-Gateway dependency is required to make the Spring Cloud Gateway in the Spring-cloud-Alibaba-Sentinel-Gateway module Automatic configuration classes take effect.

. At the same time please send spring cloud. Sentinel. Filter. The enabled configuration item is set to false (if the gateway saw URL resources flow control console, this configuration item is not set to false).


      
<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">

 <! -- Inheriting parent dependencies -->  <parent>  <artifactId>gateway-demo</artifactId>  <groupId>com.example</groupId>  <version>1.0 the SNAPSHOT</version>  </parent>  <modelVersion>4.0.0</modelVersion>   <artifactId>gateway-server-sentinel</artifactId>   <! -- Project dependencies -->  <dependencies>  <! -- Spring Cloud Gateway dependencies -->  <dependency>  <groupId>org.springframework.cloud</groupId>  <artifactId>spring-cloud-starter-gateway</artifactId>  </dependency>  <! -- Spring Cloud Alibaba NacOS Discovery  <dependency>  <groupId>com.alibaba.cloud</groupId>  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>  </dependency>  <! -- Used alone -->  <! -- Sentinel Gateway Adapter dependency -->  <dependency>  <groupId>com.alibaba.csp</groupId>  <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>  </dependency>  <! -- Used with Sentinel Starter -->  <! -- <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> Copy the code

The configuration file

server:
  port: 9001 # port

spring:
 application:
 name: gateway-server-sentinel # app name  cloud:  sentinel:  filter:  enabled: false  gateway:  discovery:  locator:  Whether to combine with the service discovery component and forward to the specific service instance via serviceId.  enabled: true # Whether to enable the routing rule based on service discovery  lower-case-service-id: true Change the service name to lowercase  # Routing rules  routes:  - id: order-service # route ID, unique  uri: lb://order-service // Get the service request address from the registry based on the service name  predicates: # Assert (judgment condition)  Append the matched request to the target URI  - Path=/order/** Copy the code

Traffic limiting rule configuration class

When using need to inject the corresponding SentinelGatewayFilter instances 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;  / * ** Traffic limiting rule configuration class* / @Configuration public class GatewayConfiguration {   private final List<ViewResolver> viewResolvers;  private final ServerCodecConfigurer serverCodecConfigurer;   / * ** the 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 handler *  * @return * /  @Bean  @Order(Ordered.HIGHEST_PRECEDENCE)  public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler(a) {  // Register the block exception handler for Spring Cloud Gateway.  return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);  }   / * ** Limited flow filter *  * @return * /  @Bean  @Order(Ordered.HIGHEST_PRECEDENCE)  public GlobalFilter sentinelGatewayFilter(a) {  return new SentinelGatewayFilter();  }   / * ** This method is executed when the Spring container is initialized* /  @PostConstruct  public void doInit(a) {  // Load the gateway traffic limiting rule  initGatewayRules();  }   / * ** Gateway traffic limiting rules* /  private void initGatewayRules(a) {  Set<GatewayFlowRule> rules = new HashSet<>();  / *Resource: indicates the resource name. It can be the route name in the gateway or user-defined API group nameCount: traffic limiting thresholdIntervalSec: indicates the statistical time window, in seconds. The default value is 1 second* /  rules.add(new GatewayFlowRule("order-service")  .setCount(3) // Traffic limiting threshold  .setIntervalSec(60)); // Statistics time window, in seconds, default is 1 second  // Load the gateway traffic limiting rule  GatewayRuleManager.loadRules(rules);  }  } Copy the code

Start the class

GatewayServerSentinelApplication.java

package com.example;

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

// Enable the EurekaClient annotation, which is enabled by default in the current version if the Eureka registry is configured //@EnableEurekaClient @SpringBootApplication public class GatewayServerSentinelApplication {   public static void main(String[] args) {  SpringApplication.run(GatewayServerSentinelApplication.class, args);  }  } Copy the code

access

Repeatedly visit: http://localhost:9001/order/1 the results are as follows:


Interface BlockRequestHandler default implementation for DefaultBlockRequestHandler, when trigger current limit returns the default error message: Blocked by Sentinel: FlowException. We can customize the exception prompt through GatewayCallbackManager.

Custom exception prompt

The setBlockHandler registration function of the GatewayCallbackManager is used to implement custom logic to handle traffic limiting 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.*;  / * ** Traffic limiting rule configuration class* / @Configuration public class GatewayConfiguration {   private final List<ViewResolver> viewResolvers;  private final ServerCodecConfigurer serverCodecConfigurer;   / * ** the 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 handler *  * @return * /  @Bean  @Order(Ordered.HIGHEST_PRECEDENCE)  public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler(a) {  // Register the block exception handler for Spring Cloud Gateway.  return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);  }   / * ** Limited flow filter *  * @return * /  @Bean  @Order(Ordered.HIGHEST_PRECEDENCE)  public GlobalFilter sentinelGatewayFilter(a) {  return new SentinelGatewayFilter();  }   / * ** This method is executed when the Spring container is initialized* /  @PostConstruct  public void doInit(a) {  // Load the gateway traffic limiting rule  initGatewayRules();  // Load the custom flow limiting exception handler  initBlockHandler();  }   / * ** Gateway traffic limiting rules* /  private void initGatewayRules(a) {  Set<GatewayFlowRule> rules = new HashSet<>();  / *Resource: indicates the resource name. It can be the route name in the gateway or user-defined API group nameCount: traffic limiting thresholdIntervalSec: indicates the statistical time window, in seconds. The default value is 1 second* /  rules.add(new GatewayFlowRule("order-service")  .setCount(3) // Traffic limiting threshold  .setIntervalSec(60)); // Statistics time window, in seconds, default is 1 second  // Load the gateway traffic limiting rule  GatewayRuleManager.loadRules(rules);  }   / * ** Custom flow limiting exception handlers* /  private void initBlockHandler(a) {  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 the custom flow limiting exception handler  GatewayCallbackManager.setBlockHandler(blockRequestHandler);  }  } Copy the code

access

Repeatedly visit: http://localhost:9001/order/1 the results are as follows:


Grouping 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.*;  / * ** Traffic limiting rule configuration class* / @Configuration public class GatewayConfiguration {   private final List<ViewResolver> viewResolvers;  private final ServerCodecConfigurer serverCodecConfigurer;   / * ** the 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 handler *  * @return * /  @Bean  @Order(Ordered.HIGHEST_PRECEDENCE)  public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler(a) {  // Register the block exception handler for Spring Cloud Gateway.  return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);  }   / * ** Limited flow filter *  * @return * /  @Bean  @Order(Ordered.HIGHEST_PRECEDENCE)  public GlobalFilter sentinelGatewayFilter(a) {  return new SentinelGatewayFilter();  }   / * ** This method is executed when the Spring container is initialized* /  @PostConstruct  public void doInit(a) {  // Load the gateway traffic limiting rule  initGatewayRules();  // Load the custom flow limiting exception handler  initBlockHandler();  }   / * ** Gateway traffic limiting rules* /  private void initGatewayRules(a) {  Set<GatewayFlowRule> rules = new HashSet<>();  / *Resource: indicates the resource name. It can be the route name in the gateway or user-defined API group nameCount: traffic limiting thresholdIntervalSec: indicates the statistical time window, in seconds. The default value is 1 second* /  // rules.add(new GatewayFlowRule("order-service")  SetCount (3) // Traffic limiting threshold  // .setIntervalSec(60)); // Statistics time window, in seconds, default is 1 second  / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- current-limiting group -- -- -- -- -- -- -- -- -- -- start -- -- -- -- -- -- -- -- -- --  rules.add(new GatewayFlowRule("product-api")  .setCount(3) // Traffic limiting threshold  .setIntervalSec(60)); // Statistics time window, in seconds, default is 1 second  rules.add(new GatewayFlowRule("order-api")  .setCount(5) // Traffic limiting threshold  .setIntervalSec(60)); // Statistics time window, in seconds, default is 1 second  / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- current-limiting group -- -- -- -- -- -- -- -- -- -- -- end -- -- -- -- -- -- -- -- -- -- --  // Load the gateway traffic limiting rule  GatewayRuleManager.loadRules(rules);  // Load the traffic limiting group  initCustomizedApis();  }   / * ** Custom flow limiting exception handlers* /  private void initBlockHandler(a) {  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 the custom flow limiting exception handler  GatewayCallbackManager.setBlockHandler(blockRequestHandler);  }   / * ** Traffic limiting packet* /  private void initCustomizedApis(a) {  Set<ApiDefinition> definitions = new HashSet<>();  / / the product - API group  ApiDefinition api1 = new ApiDefinition("product-api")  .setPredicateItems(new HashSet<ApiPredicateItem>() {{  // Matches /product-service/product with all requests for its subpath  add(new ApiPathPredicateItem().setPattern("/product-service/product/**")  .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));  }});   / / the order - API group  ApiDefinition api2 = new ApiDefinition("order-api")  .setPredicateItems(new HashSet<ApiPredicateItem>() {{  // Only /order-service/order/index is matched  add(new ApiPathPredicateItem().setPattern("/order-service/order/index"));  }});  definitions.add(api1);  definitions.add(api2);  // Load the traffic limiting group  GatewayApiDefinitionManager.loadApiDefinitions(definitions);  }  } Copy the code

access

Visit: http://localhost:9001/product-service/product/1 “trigger current limit”

Visit: http://localhost:9001/order-service/order/index “trigger current limit”

Visit: http://localhost:9001/order-service/order/1 “will not trigger current limit”

This concludes the Sentinel Service sentry tutorial.


This article is licensed under a Creative Commons attribution – Noncommercial – No Deductive 4.0 International license.

You can see more articles about Spring Cloud in the category.

🤗 your likes and retweets are the biggest support for me.

📢 Scan code pay attention to Mr. Hallward “document + video” each article is equipped with a special video explanation, learning more easily oh ~