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 ),访问还是会被限流,以此类推。 排队的流控效果多用于对一些响应不太稳定的服务做流控。
如果博主有说错的地方或者大家有不同的见解,欢迎大家评论补充。
|