Sentinel(分布式系统的流量防卫兵)
是阿里开源的一套用于服务容错的综合性解决方案。它以流量 为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。
笔记1:
Sentinel 具有以下特征:
1.丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景, 例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
2.完备的实时监控:Sentinel 提供了实时的监控功能。通过控制台可以看到接入应用的单台机器秒级数据, 甚至 500 台以下规模的集群的汇总运行情况。
3.广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块, 例如与 SpringCloud、Dubbo、gRPC 的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。
4.完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 分为两个部分:
1.核心库(Java 客户端):不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
2.控制台(Dashboard):基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
笔记2:
Sentinel是阿里巴巴开发的一个开源服务器,用于给微服务体系提供服务容错的解决方案,从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。
在使用Sentinel 之前, 必须先 安装 Sentinel控制台并启动!
注意: Sentinel 是个springboot项目,因此可以解压后通过IDEA导入项目,再运行启动类启动,或者不需解压,直接在控制台运行jar包启动(建议)!
# 直接在cmd命令窗口使用jar命令启动项目,因为Sentinle本身是SpringBoot项目(先cd到jar包目录)
# 我下载的是1.7.2的版本
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.2.jar
扩展:
-Dsentinel.dashboard.auth.username=
sentinel //
用于指定控制台的登录用户名为 sentinel;-Dsentinel.dashboard.auth.password=
123456 //
用于指定控制台的登录密码为 123456;如果省略这两个参数,默认用户和密码均为 sentinel-Dserver.servlet.session.timeout=7200 //
用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟;
启动成功:
输入地址 localhost:8080 访问Sentinel控制台(注意:用户名密码都是: sentinel):
依赖:
<!--Sentinel组件-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
server:
port: 8091
tomcat:
max-threads: 10 #tomcat的最大并发值修改为10,默认是200
spring:
application:
name: service-order
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/shop?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: root
cloud:
nacos: #nacos的作用就是一个注册中心
discovery:
server-addr: 127.0.0.1:8848
sentinel:
#Sentinel是个springboot项目的控制台, 提供服务容错方案,通过流量控制、熔断降级、系统负载保护来维持服务的稳定性
#cmd命令窗口启动Sentinel控制台命令:(cd 到 jar包所在目录)
#java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.0.jar
transport:
port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可
dashboard: localhost:8080 # 指定Sentinel服务地址(要和-Dserver.port端口一致)
service-product: # 调用外部服务的名称
ribbon: #配置 ribbon
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
#设置负载均衡策略 BestAvailableRule 访问最小并发请求的服务; RandomRule 随机访问服务等策略
jpa:
properties:
hibernate:
hbm2ddl:
auto: update
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
调用controller层的接口,然后在新的标签页中访问 localhost:8080,才能显示出被调用的接口对应的微服务.
(不知道是不是google浏览器的问题,调用接口后刷新当前标签,居然不显示微服务的!!!!!!)
注意:如果设置 -Dserver.port 和 -Dcsp.sentinel.dashboard.server 的端口一样,则会把sentinel自带的控制台服务 sentinel-dashboard 也显示出来;
如果不想看见该服务,则可以设置不同的端口号,或者启动命令中不带 -Dcsp.sentinel.dashboard.server
笔记3:
实时监控:
经常会发现sentinel的实时监控没有数据,是一片空白的,这是因为:
1.浏览器问题,比如我用的google浏览器就不能实时更新,而是要自己去快速的手动刷新.
2.时间问题,sentinel是实时监控的,只有在接口调用的瞬间,马上刷新sentinle控制台的页面才有可能看得见.(太过拙劣了,以后得想办法看看如何解决)
笔记4:
簇点链路:
该选项中可以对具体的接口实行流量控制,熔断降级等操作,用于管理控制访问资源(是sentinel的保护对象,可以是服务,也可以是接口或一段代码)的请求数量.
这里我对 /order/message接口 选择流控:
资源名:唯一名称,默认是请求路径,可自定义.
针对来源:指定对哪个微服务进行限流,默认指default,意思是不区分来源,全部限制.
阈值类型/单机阈值:
QPS(每秒请求数量): 当调用该接口的每秒请求数达到阈值的时候,进行限流
线程数:当调用该接口的总线程数达到阈值的时候,进行限流
是否集群:暂不需要集群
当我设置了流控规则为:每秒请求数量不得超过2,否则限流;
然后快速访问接口,会出现以下信息,就是限流成功.
笔记5:
流控模式(规则)有3种:
在规则编辑界面,点击高级选项,可以看见流控模式和流控效果:
直接(默认):当前资源(接口)达到限流条件时,开启限流,最简单的模式,这里不介绍.
关联:当关联的资源(接口)达到限流条件时,开启限流 [适合做应用让步],
1.配置限流规则, 将流控模式设置为关联,关联资源设置为的 /order/message1
2.然后快速访问 /order/message1 接口, 再访问回 /order/message, 就会发生限流:
链路:当从某个(资源)接口过来的请求量达到限流条件时,开启限流(和关联流控的区别在于: 链路流控是针对上级接口,而关联流控可以是已注册的任意接口)
注意:
Sentinel1.7.0 版本开始(对应SCA的2.1.1.RELEASE),官方在CommonFilter 引入了 WEB_CONTEXT_UNIFY 参数,用于控制是否收敛context。将其配置为 false 即可根据不同的 URL 进行链路限流。
SCA(spring-cloud-alibaba) 2.1.1.RELEASE之后的版本,可以通过配置spring.cloud.sentinel.web-context-unify=false即 可关闭收敛.
所以2.1.1.RELEASE之前的版本无法配置链路限流.
创建OrderServiceImpl3 和 OrderController3,测试链路流控.
@Service
public class OrderServiceImpl3 {
/*
@SentinelResource("资源名")注解: 用于指定本地资源(接口)并配置相应的流控规则.
*/
@SentinelResource("/message")
public void message() { System.out.println("message"); }
}
@RestController
@Slf4j
public class OrderController3 {
@Autowired
private OrderServiceImpl3 orderServiceImpl3;
@RequestMapping("/order/message1")
public String message1() { return "message1"; }
@RequestMapping("/order/message2")
public String message2() { return "message2"; }
@RequestMapping("/order/message3")
public String message3() {
orderServiceImpl3.message();
return "message3";
}
}
配置message的链路流控规则
快速访问发现message3会被限流,但message2不会:
流控效果有3种:
快速失败(默认): 直接失败,抛出异常,不做任何额外的处理,是最简单的效果
Warm Up:它从开始阈值到最大QPS阈值会有一个缓冲阶段,一开始的阈值是最大QPS阈值的 1/3,然后慢慢增长,直到最大阈值,适用于将突然增大的流量转换为缓步增长的场景。
排队等待:让请求以均匀的速度通过,单机阈值为每秒通过数量,其余的排队等待; 它还会让设 置一个超时时间,当请求超过超时间时间还未处理,则会被丢弃。
笔记6:
降级模式(规则):
降级就是降低服务的优先级的意思,让优先级更高的服务能正常顺利运行.
有3种条件:
平均响应时间(RT) :当资源的平均响应时间超过阈值(以 ms 为单位)之后,资源进入准降级状态。 如果接下来1s 内持续进入 5 个请求,它们的 RT都持续超过这个阈值,那么在接下的时间窗口 (以 s 为单位)之内,就会对这个方法进行服务降级。
[注意: Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要 变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。]
以上设置为:当平均响应时间(RT)>1ms,接下来10s内该服务降级,10s后恢复正常,并进入下一轮判断!
异常比例:当资源的每秒异常总数占通过量的比值超过阈值之后,资源进入降级状态,即在接下的时间窗口(以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0]。
以上设置为:当每秒异常数超过0.25,则降级,在10s内,自动返回对该接口的调用.
异常数 :当资源近 1 分钟的异常数目超过阈值之后会在接下来的指定时间内进行服务降级。
[注意: 由于统计时间窗口是分钟级别的,若时间窗口小于 60s,则可能导致在接下来统计的60s内又检测到异常数超过阈值(降级不会取消该检测),这样一来即使结束熔断状态(恢复正常),也可能会再进入熔断状态。]
以上设置为:在近60s内,异常数超过3,则在70s内降级.
问题:流控规则和降级规则返回的异常页面是一样的,我们怎么来区分到底是什么原因导致的呢?
解法:可以编写自定义异常处理逻辑,通过捕获的异常类型就可以判断出相应的异常问题:
1.创建异常处理类:
/**
* 自定义sentinel异常返回信息
*/
@Component
public class ExceptionHandlerPage implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException e) throws IOException {
// BlockException 异常接口,其子类为Sentinel五种规则异常的实现类
// AuthorityException 授权异常
// DegradeException 降级异常
// FlowException 限流异常
// ParamFlowException 参数限流异常
// SystemBlockException 系统负载异常
ResponseData data = new ResponseData();
if ( e instanceof FlowException ) {
data = new ResponseData(-1, "接口被限流了。");
} else if ( e instanceof DegradeException ) {
data = new ResponseData(-2, "接口被降级了。");
}
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(JSON.toJSONString(data));
}
}
2.创建返回实体类:
/**
* 定义返回的实体类,字段根据需要添加
*/
@Data// 生成getter/setter/tostring/equals
@AllArgsConstructor // 全参构造
@NoArgsConstructor// 无参构造
public class ResponseData {
private int code;
private String message;
}
3.配置RT降级规则和QPS流控规则,再快速调用接口:
笔记7:
热点规则:
热点参数流控规则是一种粒度更细的流控规则, 它允许将规则具体到接口的参数上。
注意:热点规则只能运用在@SentinelResource注解标识的资源上.
1.新建接口
@RequestMapping("/order/message4")
@SentinelResource("message4")//必须使用@SentinelResource注解标识,否则热点规则不生效
public String message3(String name, Integer age) { return name + age; }
2.配置热点规则
以上设置为:对接口的第一个参数进行流控,若调用接口时第一个参数传了参,且每秒接口请求数大于1,则限流.
快速访问,传了第一个参name和不传第一个参的情况:发现只有第一个参数会被限流.
参数例外项:
允许对一个参数的具体值进行流控.编辑刚才定义的规则,增加参数例外项:
以上设置为:当该参数(第一个参数)是String类型,且参数值为: stephen 时,设置QPS>1000时,才会限流.
快速访问接口,并给第一个参数(name)传参为stephen: 发现不再限流; 而参数不为stephen时, 则会限流.
笔记8:
授权规则(黑白名单):
很多时候,我们需要根据调用来源(外部服务的接口)来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单授权规则)的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过:
白名单: 只有白名单记录的请求来源可通过, 其余的请求不可通过;
黑名单: 只有黑名单记录的请求来源不可通过, 其余的请求可通过.
1.自定义来源处理规则
/*
在Sentinel的授权规则配置中,有个流控应用的项:用于标识调用本服务接口的外部服务来源.
Sentinel提供了 RequestOriginParser 接口来处理来源。
只要Sentinel保护的接口资源被访问,Sentinel就会调用 RequestOriginParser 的实现类去解析访问来源。
*/
@Component
public class RequestOriginParserDefinition implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
//获取请求中传入的参数
String serviceName = request.getParameter("name");
return serviceName;
}
}
2.配置授权规则
3.调用接口发现: 当传入name=stephen,则失败;只有传入name=william 成功.
笔记9:
系统规则:
系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load、RT、入口 QPS 、CPU 使用率和线程数五个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量 (进入应用的流量) 生效。
Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过 系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般 是 CPU cores * 2.5。
RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
CPU使用率:当单台机器上所有入口流量的 CPU使用率达到阈值即触发系统保护。