== Spring Cloud Alibaba Sentinel

### Introduction of Sentinel

As microservices become popular, the stability of service calls is becoming increasingly important. https://github.com/alibaba/Sentinel[Sentinel] takes "flow" as the breakthrough point, and works on multiple fields including flow control, circuit breaking and load protection to protect service reliability.

https://github.com/alibaba/Sentinel[Sentinel] has the following features:


* *Rich Scenarios*： Sentinel has supported the key scenarios of Alibaba’s Double 11 Shopping Festivals for over 10 years, such as second kill(i.e., controlling sudden bursts of traffic flow so that it’s within the acceptable range of the system capacity), message load shifting, circuit breaking of unreliable downstream applications.
* *Comprehensive Real-Time Monitoring*： Sentinel provides real-time monitoring capability. You can see the monitoring data of your servers at the accuracy of seconds, and even the overall runtime status of a cluster with less than 500 nodes.
* *Extensive Open-Source Ecosystem*： Sentinel provides out-of-box modules that can be easily integrated with other open-source frameworks/libraries, such as Spring Cloud, Dubbo, and gRPC. To use Sentinel, you only need to introduce the related dependency and make a few simple configurations.
* *Sound SPI Extensions*： Sentinel provides easy-to-use and sound SPI extension interfaces. You can customize logics with the SPI extensions quickly, for example, you can define your own rule management, or adapt to specific data sources.

### How to Use Sentinel

If you want to use Sentinel in your project, please use the starter with the group ID as `org.springframework.cloud` and the artifact ID as `spring-cloud-starter-alibaba-sentinel`.

```xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
```

The following is a simple example of how to use Sentinel:

```java
@SpringBootApplication
public class Application {

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

}

@RestController
public class TestController {

    @GetMapping(value = "/hello")
    @SentinelResource("hello")
    public String hello() {
        return "Hello Sentinel";
    }

}
```

The @SentinelResource annotation is used to identify if a resource is rate limited or degraded. In the above sample, the 'hello' attribute of the annotation refers to the resource name.

@SentinelResource also provides attributes such as `blockHandler`, `blockHandlerClass`,  and `fallback` to identify rate limiting or degradation operations. For more details, refer to  https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81[Sentinel Annotation Support].

##### Sentinel Dashboard

Sentinel dashboard is a lightweight console that provides functions such as machine discovery, single-server resource monitoring, overview of cluster resource data, as well as rule management. To use these features, you only need to complete a few steps.

*Note*: The statistics overview for clusters only supports clusters with less than 500 nodes, and has a latency of about 1 to 2 seconds.

.Sentinel Dashboard
image::https://github.com/alibaba/Sentinel/wiki/image/dashboard.png[]

To use the Sentinel dashboard, simply complete the following 3 steps.

###### Get the Dashboard

You can download the latest dashboard JAR file from the https://github.com/alibaba/Sentinel/releases[Release Page].

You can also get the latest source code to build your own Sentinel dashboard：

* Download the  https://github.com/alibaba/Sentinel/tree/master/sentinel-dashboard[Dashboard] project.
* Run the following command to package the code into a FatJar: `mvn clean package`


###### Start the Dashboard

Sentinel dashboard is a standard SpringBoot application, and you can run the JAR file in the Spring Boot mode.

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

If there is conflict with the 8080 port, you can use `-Dserver.port=new port` to define a new port.

#### Configure the Dashboard

.application.yml
----
spring:
  cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: localhost:8080
----

The port number specified in `spring.cloud.sentinel.transport.port` will start an HTTP Server on the corresponding server of the application, and this server will interact with the Sentinel dashboard. For example, if a rate limiting rule is added in the Sentinel dashboard, the the rule data will be pushed to and recieved by the HTTP Server, which in turn registers the rule to Sentinel.

For more information about Sentinel dashboard, please refer to https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0[Sentinel Dashboard].

### Feign Support

Sentinel is compatible with the https://github.com/OpenFeign/feign[Feign] component. To use it, in addition to introducing the `sentinel-starter` dependency, complete the following 3 steps:

* Enable the Sentinel support for feign in the properties file. `feign.sentinel.enabled=true`
* Add the `feign starter` dependency to trigger and enable `sentinel starter`:
```xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
```
* The `loadbalance` in `feign` is dependent on the `ribbon` module of Netflix (You do not need to introduce it if `loadbalance` is not used)：
```xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
```

This is an interface for `FeignClient`:

```java
@FeignClient(name = "service-provider", fallback = EchoServiceFallback.class, configuration = FeignConfiguration.class)
public interface EchoService {
    @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
    String echo(@PathVariable("str") String str);
}

class FeignConfiguration {
    @Bean
    public EchoServiceFallback echoServiceFallback() {
        return new EchoServiceFallback();
    }
}

class EchoServiceFallback implements EchoService {
    @Override
    public String echo(@PathVariable("str") String str) {
        return "echo fallback";
    }
}
```

NOTE: The resource name policy in the corresponding interface of Feign is：httpmethod:protocol://requesturl

The corresponding resource name of the `echo` method in the `EchoService` interface is `GET:http://service-provider/echo/{str}`.

Note: All the attributes in the `@FeignClient` annotation is supported by Sentinel.

### RestTemplate Support

Spring Cloud Alibaba Sentinel supports the protection of `RestTemplate` service calls using Sentinel. To do this, you need to add the `@SentinelRestTemplate` annotation when constructing the `RestTemplate` bean.

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

The parameter of the `@SentinelRestTemplate` annotation support flow control(`blockHandler`, `blockHandlerClass`) and circuit breaking(`fallback`, `fallbackClass`).

==

The `blockHandler` or `fallback` is the static method of `blockHandlerClass` or `fallbackClass`.

The parameter of method in `@SentinelRestTemplate` is same as `ClientHttpRequestInterceptor`, but it has one more parameter `BlockException` and its value of return type should be `ClientHttpResponse`.
If you do not continue to execute the following Http request interceptor after being restricted, you can use the instance of `SentinelClientHttpResponse` to customize the return value result after the current limit.

The method signature of `handleException` in `ExceptionUtil` above should be like this:

```java
public class ExceptionUtil {
    public static ClientHttpResponse handleException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException exception) {
        ...
    }
}
```

It will return `RestTemplate request block by sentinel` when you do not write any flow control(`blockHandler`, `blockHandlerClass`) configuration or circuit breaking configuration(`fallback`, `fallbackClass`), you can override it by using your own method.

Sentinel RestTemplate provides two granularities for resource rate limiting:

* `schema://host:port/path`： Protocol, host, port and path

* `schema://host:port`： Protocol, host and port

NOTE: Take `https://www.taobao.com/test` as an example. The corresponding resource names have two levels of granularities, `https://www.taobao.com` and `https://www.taobao.com/test`.

### Dynamic Data Source Support

#### The usage of our first version 0.2.0.RELEASE or 0.1.0.RELEASE

you need to complete the following 3 steps to configure your data source.

* Define the data source information in your properties file. For example, you can use:

.application.properties
----
spring.cloud.sentinel.datasource.type=file
spring.cloud.sentinel.datasource.recommendRefreshMs=3000
spring.cloud.sentinel.datasource.bufSize=4056196
spring.cloud.sentinel.datasource.charset=utf-8
spring.cloud.sentinel.datasource.converter=flowConverter
spring.cloud.sentinel.datasource.file=/Users/you/yourrule.json
----

* Create a Converter class to implement the `com.alibaba.csp.sentinel.datasource.Converter` interface, and you need to have a bean of this class in `ApplicationContext`.

```java
@Component("flowConverter")
public class JsonFlowRuleListParser implements Converter<String, List<FlowRule>> {
	@Override
	public List<FlowRule> convert(String source) {
		return JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
		});
	}
}
```

The bean name of this Converter needs to be the same with the converter in the `application.properties` file.

* Define a `ReadableDataSource` attribute that is modified by the `@SentinelDataSource` annotation in any Spring Bean `@SentinelDataSource`.

```java
@SentinelDataSource("spring.cloud.sentinel.datasource")
private ReadableDataSource dataSource;
```

The value attribute of `@SentinelDataSource` means that the data source is in the prefix of the `application.properties` file. In this example, the prefix is `spring.cloud.sentinel.datasource`.

If there are over 1 `ReadableDataSource` beans in `ApplicationContext`, then none of the data sources in `ReadableDataSource` will be loaded. It takes effect only when there is only 1 `ReadableDataSource` in `ApplicationContext`.

If the data source takes effect and is loaded successfully, the dashboard will print information as shown below:

```
[Sentinel Starter] load 3 flow rules
```

#### The usage after first version

You only need to complete 1 step to configure your data source:

* Configure your data source in the `application.properties` file directly.

For example, 4 data sources are configures：

```
spring.cloud.sentinel.datasource.ds1.file.file=classpath: degraderule.json

spring.cloud.sentinel.datasource.ds2.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds2.nacos.dataId=sentinel
spring.cloud.sentinel.datasource.ds2.nacos.groupId=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds2.nacos.data-type=json

spring.cloud.sentinel.datasource.ds3.zk.path = /Sentinel-Demo/SYSTEM-CODE-DEMO-FLOW
spring.cloud.sentinel.datasource.ds3.zk.server-addr = localhost:2181

spring.cloud.sentinel.datasource.ds4.apollo.namespace-name = application
spring.cloud.sentinel.datasource.ds4.apollo.flow-rules-key = sentinel
spring.cloud.sentinel.datasource.ds4.apollo.default-flow-rule-value = test

```

This method follows the configuration of Spring Cloud Stream Binder. `TreeMap` is used for storage internally, and comparator is `String.CASE_INSENSITIVE_ORDER`.

NOTE: d1, ds2, ds3, ds4 are the names of `ReadableDataSource`, and can be coded as you like. The `file`, `zk`, `nacos` , `apollo` refer to the specific data sources. The configurations following them are the specific configurations of these data sources respecitively.

Every data source has two common configuration items: `data-type` and `converter-class`.

`data-type` refers to `Converter`. Spring Cloud Alibaba Sentinel provides two embedded values by defaul: `json` and `xml` (the default is json if not specified). If you do not want to use the embedded `json` or `xml` `Converter`, you can also fill in `custom` to indicate that you will define your own `Converter`, and then configure the `converter-class`. You need to specify the full path of the class for this configuration.

The two embedded `Converter` only supports parsing the Json array or XML array. Sentinel will determine automatically which of the 4 Sentinel rules that the Json or XML objext belongs to(`FlowRule`, `DegradeRule`, `SystemRule`, `AuthorityRule`, `ParamFlowRule`).

For example, if 5 rate limiting rules and 5 degradation rules in the 10 rule arrays,  then the data source will not be registered, and there will be warnings in the logs.

If there are 9 rate limiting rules among the 10 arrays, and 1 error parsing the 10th array, then there will be warning in the log indicating that there is error in one of the rules, while the other 9 rules will be registered.

Here `jackson` is used to parse the Json or XML arrays. `ObjectMapper` or `XmlMapper` uses the default configuration and will report error if there are unrecognized fields. The reason behind this logic is that without this strict parsing, Json will also be parsed as system rules(all system rules have default values).

In another case, the Json or XML object might match all of the 4 rules(for example, if only the `resource` field is configured in the Json object, then it will match all 4 rules). In this case, there will be warnings in the log, and this object will be filtered.

When you use this configuration, you only need to fill in the Json or XML correctly, and any of the unreasonable information will be printed in the log.

If the data source takes effect and is loaded successfully, the dashboard will print information as shown below:

```
[Sentinel Starter] DataSource ds1-sentinel-file-datasource load 3 DegradeRule
[Sentinel Starter] DataSource ds2-sentinel-nacos-datasource load 2 FlowRule
```

NOTE: XML format is not supported by default. To make it effective, you need to add the  `jackson-dataformat-xml` dependency.

To learn more about how dynamic data sources work in Sentinel, refer to https://github.com/alibaba/Sentinel/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%99%E6%89%A9%E5%B1%95[Dynamic Rule Extension].

### Endpoint Support

Before you use the Endpoint feature, please add the  `spring-boot-starter-actuator` dependency in Maven, and enable access of Endpoints in your configuration.

* Add `management.security.enabled=false` in Spring Boot 1.x. The exposed endpoint path is `/sentinel`.
* Add `management.endpoints.web.exposure.include=*` in Spring Boot 2.x. The exposed endpoint path is `/actuator/sentinel`.

### More

The following table shows that when there are corresponding bean types in `ApplicationContext`, some actions will be taken:

:frame: topbot
[width="60%",options="header"]
|====
^|Existing Bean Type ^|Action ^|Function
|`UrlCleaner`|`WebCallbackManager.setUrlCleaner(urlCleaner)`|Resource cleaning(resource(for example, classify all URLs of /foo/:id to the  /foo/* resource))
|`UrlBlockHandler`|`WebCallbackManager.setUrlBlockHandler(urlBlockHandler)`|Customize rate limiting logic
|====

The following table shows all the configurations of Spring Cloud Alibaba Sentinel:

:frame: topbot
[width="60%",options="header"]
|====
^|Configuration ^|Description ^|Default Value
|`spring.cloud.sentinel.enabled`|Whether Sentinel automatic configuration takes effect|true
|`spring.cloud.sentinel.eager`|Cancel Sentinel dashboard lazy load|false
|`spring.cloud.sentinel.transport.port`|Port for the application to interact with Sentinel dashboard. An HTTP Server which uses this port will be started in the application|8721
|`spring.cloud.sentinel.transport.dashboard`|Sentinel dashboard address|
|`spring.cloud.sentinel.transport.heartbeatIntervalMs`|Hearbeat interval between the application and Sentinel dashboard|
|`spring.cloud.sentinel.filter.order`|Loading order of Servlet Filter. The filter will be constructed in the Starter|Integer.MIN_VALUE
|`spring.cloud.sentinel.filter.spring.url-patterns`|Data type is array. Refers to the collection of Servlet Filter ULR patterns|/*
|`spring.cloud.sentinel.metric.charset`|metric file character set|UTF-8
|`spring.cloud.sentinel.metric.fileSingleSize`|Sentinel metric single file size|
|`spring.cloud.sentinel.metric.fileTotalCount`|Sentinel metric total file number|
|`spring.cloud.sentinel.log.dir`|Directory of Sentinel log files|
|`spring.cloud.sentinel.log.switch-pid`|If PID is required for Sentinel log file names|false
|`spring.cloud.sentinel.servlet.blockPage`| Customized redirection URL. When rate limited, the request will be redirected to the pre-defined URL |
|`spring.cloud.sentinel.flow.coldFactor`| https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8[冷启动因子] |3
|====
