什么是sentinel?
Sentinel: 分布式系统的流量防卫兵
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 的主要特性:
Sentinel 分为两个部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
- 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
github地址
中文介绍
安装Sentinel
- 首先去下载,地址
- 然后运行,运行命令
java -jar sentinel-dashboard-1.8.2.jar
前提是有jdk环境,并且8080端口不能被占用
然后去访问http://localhost:8080
用户名密码都是sentinel
项目整合sentinel
-
新建模块
cloudalibaba-sentinel-service8401
-
修改pom文件,添加以下的依赖
<dependencies>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.zhubayi.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- SpringBoot整合Web组件+actuator -->
<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>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- 修改配置文件
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
#Nacos服务注册中心地址
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719
management:
endpoints:
web:
exposure:
include: '*'
- 新建
MainApp8401
主启动类
package com.zhubayi.springcloud.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401
{
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class, args);
}
}
- 新建
FlowLimitController
类
package com.zhubayi.springcloud.alibaba.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FlowLimitController
{
@GetMapping("/testA")
public String testA()
{
return "------testA";
}
@GetMapping("/testB")
public String testB()
{
return "------testB";
}
}
然后启动Sentinel8080
java -jar sentinel-dashboard-1.8.2.jar
登录进去发现啥都没有
因为Sentinel采用的懒加载,当有访问时才会加载,所以执行一次访问即可http://localhost:8401/testA
http://localhost:8401/testB
说明sentinel8080正在监控微服务8401。
流控规则
1.基本介绍
解释说明:
2. 流控模式
(1) 直接(默认)
- 就是快速失败,直接不让访问了,这是
系统默认
的 - 配置说明
表示1秒钟内查询1次就是OK,若超过次数1,就直接-快速失败,报默认错误
测试:快速点击访问http://localhost:8401/testA
直接调用默认报错信息
(2) 关联
关联是啥意思?
- 当关联的资源达到阈值时,就限流自己。例如:当与A关联的资源B达到阀值后,就限流A自己。B惹事,A挂了
- 配置A
说明:当关联资源/testB的qps阀值超过1时,就限流/testA的Rest访问地址,当关联资源到阈值后限制配置好的资源名
postman模拟并发密集访问testB
开始运行然后去访问testA
可以看出,大批量线程高并发访问B,导致A失效了
(3) 链路
链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流。它的功能有点类似于针对来源配置项,区别在于:针对来源是针对上级微服务,而链路流控是针对上级接口,也就是说它的粒度更细。
这是重点讲的内容 毕竟在这里踩过坑
我用的版本是springCloud-alibab 是2.2.5.RELEASE sentinel1.8.0 所以会造成流控链路失效
给出解决办法
Sentinel 链路流控模式失效
禁止收敛URL的入口context
从1.6.3 版本开始,Sentinel Web filter默认收敛所有URL的入口context,因此链路限流不生效。
1.7.0 版本开始(对应SCA的2.1.1.RELEASE),官方在CommonFilter 引入了
WEB_CONTEXT_UNIFY 参数,用于控制是否收敛context。将其配置为 false 即可根据不同的URL 进行链路限流。
SCA 2.1.1.RELEASE之后的版本,可以通过配置spring.cloud.sentinel.web-context-unify=false即可关闭收敛
我们当前使用的版本是SpringCloud Alibaba 2.2.5.RELEASE,无法实现链路限流。
新增service接口
package com.zhubayi.springcloud.alibaba.serivce;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.stereotype.Service;
@Service
public class MySentinelService {
/**
* @SentinelResource: 可以理解就是一个资源名
*/
@SentinelResource(value = "myresource",blockHandler = "testAFallback")
public String sentinelChain() {
return "调用该资源成功!!!!!";
}
public String testAFallback(BlockException ex) {
return "ex testA";
}
}
- controller
package com.zhubayi.springcloud.alibaba.controller;
import com.zhubayi.springcloud.alibaba.serivce.MySentinelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FlowLimitController
{
@Autowired
private MySentinelService mySentinelService;
@GetMapping("/testA")
public String testA()
{
return "------testA"+mySentinelService.sentinelChain();
}
@GetMapping("/testB")
public String testB()
{
return "------testB"+mySentinelService.sentinelChain();
}
}
如果单单是这样的配置 是不起效果的 还需要新增配置
1.7.0 版本开始(对应Spring Cloud Alibaba的2.1.1.RELEASE) 需要新增依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
</dependency>
然后配置spring.cloud.sentinel.web-context-unify=false
配置类FilterContextConfig
package com.zhubayi.springcloud.alibaba.config;
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 FilterContextConfig {
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new CommonFilter());
registrationBean.addUrlPatterns("/*");
// 入口资源关闭聚合
registrationBean.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
registrationBean.setName("sentinelFilter");
registrationBean.setOrder(1);
return registrationBean;
}
}
接下来就有效果了
测试:频繁访问testA会报错,而访问testB则无影响。
3.流控效果
(1)直接->快速失败
- 这时默认的处理方式,默认的流控处理
- 直接失败,抛出异常->Blocked by Sentinel (flow limiting)
- 源码:
com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
(2) Warm up 预热
当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的的。
默认 coldFactor 为 3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。
公式:阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值
例如:
上面的是官方说法 说人话就是 10/3=3
,即刚开始的阈值为3
你一开始请求超过每秒3次
则报错 等预热5秒钟
,5秒之后,超过每秒10次
系统报错,若不超过每秒10次,则正常运行
比如你要跑100米 得先热热身 让身体适应一下 不然抽筋猝死就gg了
应用场景: 秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阀值增长到设置的阀值。
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。详细文档可以参考 流量控制 - Warm Up 文档,具体的例子可以参见 WarmUpFlowDemo。
通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
(3)排队等待
匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考 流量控制 - 匀速器模式,具体的例子可以参见 PaceFlowDemo。
该方式的作用如下图所示:
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
注意:匀速排队模式暂时不支持 QPS > 1000 的场景。
例如:
说明:
匀速排队,让请求以均匀的速度通过,阀值类型必须设成QPS,否则无效。
设置含义:/testA每秒1次请求,超过的话就排队等待,等待的超时时间为20000毫秒,超过则报错。