Spring Cloud Alibaba:整合Sentinel & 流控规则详细介绍
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel具有以下特征:
- 丰富的应用场景:
Sentinel承接了阿里巴巴近10年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。 - 完备的实时监控:
Sentinel同时提供实时的监控功能。可以在控制台中看到接入应用的单台机器秒级数据,甚至500台以下规模的集群的汇总运行情况。 - 广泛的开源生态:
Sentinel提供开箱即用的与其它开源框架/库的整合模块,例如与Spring Cloud、Apache Dubbo、gRPC、Quarkus的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。同时Sentinel提供Java/Go/C++等多语言的原生实现。 - 完善的
SPI扩展机制:Sentinel提供简单易用、完善的SPI扩展接口。可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel的主要特性:  Sentinel的开源生态:  Sentinel分为两个部分:
- 核心库(
Java 客户端)不依赖任何框架/库,能够运行于所有Java运行时环境,同时对Dubbo / Spring Cloud等框架也有较好的支持。 - 控制台(
Dashboard)基于Spring Boot开发,打包后可以直接运行,不需要额外的Tomcat等应用容器。
启动Sentinel
Spring Cloud Alibaba版本关系:
博主这边使用的是2.2.6.RELEASE版本的Spring Cloud Alibaba,因此需要下载1.8.1版本的Sentinel,以及使用兼容的2.3.2.RELEASE版本的Spring Boot。
  启动Sentinel:
 服务端口为8080:  访问http://localhost:8080(用户名和密码都是sentinel):  需要等应用启动并且应用的服务被请求后,Sentinel控制台才会有展示数据。 
整合Sentinel
创建一个maven工程作为父module,pom.xml如下所示:
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.kaven</groupId>
<artifactId>alibaba</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<description>Spring Cloud Alibaba</description>
<modules>
<module>sentinel</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring-cloud-alibaba-version>2.2.6.RELEASE</spring-cloud-alibaba-version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
再创建一个maven工程作为子module(需要整合Sentinel的模块)。 
pom.xml如下所示:
<?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">
<parent>
<artifactId>alibaba</artifactId>
<groupId>com.kaven</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sentinel</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
</project>
application.yml:
server:
port: 8095
management:
endpoints:
web:
exposure:
include: "*"
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080
log:
dir: ./logs
application:
name: sentinel
MessageController类:
package com.kaven.alibaba.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MessageController {
@GetMapping("message")
public String getMessage() {
return "hello sentinel";
}
}
启动类Application:
package com.kaven.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
启动应用:  多次请求接口http://localhost:8095/message:  Sentinel控制台就会展示这些请求的统计信息:  
流控规则
添加流控规则: 
 阈值类型中QPS表示每秒访问的次数,线程数表示同时可以访问的线程的数量,单机阈值则为具体的数值,如下图所示:   快速访问接口http://localhost:8095/message,超过指定QPS会快速失败(返回字符串Blocked by Sentinel (flow limiting)): 
流控模式
 点击高级选项。  
直接模式
默认的流控模式,直接限制资源名的QPS或者线程数,上面已经演示过了。
关联模式
如果关联资源的访问超过指定的QPS或者线程数,则该资源也会被Sentinel限流。先增加一个接口:
@GetMapping("kaven")
public String getKaven() {
return "hello kaven";
}

博主使用Postman来进行测试,每访问一次/kaven资源延迟700ms再继续访问它,一共访问1000次。  当访问/kaven资源的QPS超过/message资源流控规则指定的QPS时,访问/message资源也会被限流,因为它们是关联的。
 而这种关联关系是单向的,即当访问/message资源的QPS超过/message资源流控规则指定的QPS时,关联的/kaven资源还是可以正常访问的,因为/kaven资源本身并没有设置流控规则。  
链路模式
之前都是基于Controller层的限流,Sentinel同样支持细粒度的从Controller到Service层的限流。
IMessageService接口(服务抽象):
package com.kaven.alibaba.service;
public interface IMessageService {
String getMessage();
}
MessageServiceImpl类(实现了IMessageService接口,服务实现):
package com.kaven.alibaba.service.impl;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.kaven.alibaba.service.IMessageService;
import org.springframework.stereotype.Service;
@Service
public class MessageServiceImpl implements IMessageService {
@SentinelResource("MessageServiceImpl")
@Override
public String getMessage() {
return "MessageServiceImpl";
}
}
注解@SentinelResource("MessageServiceImpl")会将Service层的getMessage方法作为Sentinel的资源,资源名为MessageServiceImpl。
修改接口:
package com.kaven.alibaba.controller;
import com.kaven.alibaba.service.IMessageService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class MessageController {
@Resource
private IMessageService messageService;
@GetMapping("message")
public String getMessage() {
return "hello sentinel " + messageService.getMessage();
}
@GetMapping("kaven")
public String getKaven() {
return "hello kaven " + messageService.getMessage();
}
}
添加依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
</dependency>
还需要一个配置类FilterConfiguration(通过过滤器实现的):
package com.kaven.alibaba;
import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfiguration {
@Bean
public FilterRegistrationBean<CommonFilter> registrationBean(){
FilterRegistrationBean<CommonFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new CommonFilter());
registrationBean.addUrlPatterns("/*");
registrationBean.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY,"false");
registrationBean.setName("sentinelFilter");
return registrationBean;
}
}
FilterRegistrationBean泛型类用于在Servlet 3.0+容器中注册Filter的ServletContextInitializer 。 类似于ServletContext提供的registration功能,但具有Spring Bean的友好设计。CommonFilter类是与Sentinel集成的Servlet过滤器。 修改配置文件:
server:
port: 8095
management:
endpoints:
web:
exposure:
include: "*"
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080
log:
dir: ./logs
filter:
enabled: false
application:
name: sentinel
重新启动应用。先请求几次接口,让Sentinel控制台有展示数据,方便给Service层的资源添加流控规则。
 给Service层的MessageServiceImpl资源添加流控规则(针对入口资源/kaven)。 访问/kaven资源超过MessageServiceImpl资源流控规则指定QPS,直接返回500状态码的响应页面。  应用后台也有日志输出:  而访问/message资源没有任何限制,因为MessageServiceImpl资源没有针对它的流控规则。
流控效果
快速失败
直接快速失败,返回字符串Blocked by Sentinel (flow limiting)或者返回500状态码的响应页面。
Warm Up
表示应用需要预热一段时间,预热时间内进行更严格的限流(相对于指定的流控规则),预热完成后恢复成指定的限流,如下图所示,QPS为3,冷加载因子为3,QPS限流阈值初始为3/3并且缓慢增加到阈值为3,时间为120s,一般用于服务器刚刚启动时的场景。  预热时间内,快速访问资源/kaven会被Sentinel限流(只要QPS超过1)。  预热完成后,访问/kaven资源QPS超过3才会被Sentinel限流,大家可以自己尝试一下。
排队等待

在超过资源流控规则指定的QPS后,等待超时时间200毫秒后,如果没有被限流则访问成功,如果被限流则访问失败。使用Postman来进行测试,每访问一次/kaven资源延迟500ms再继续访问它,一共访问100次。  第一次访问成功,第二次访问超过资源流控规则指定的QPS,会被限流,等待超时时间200毫秒后(500ms + 200ms < 1000ms),访问还是会被限流,以此类推。  排队的流控效果多用于对一些响应不太稳定的服务做流控。
如果博主有说错的地方或者大家有不同的见解,欢迎大家评论补充。
|