赞
踩
Spring Cloud Alibaba Sentinel 是一个开源的流量控制和熔断框架,它是 Alibaba 开源的微服务框架 Spring Cloud Alibaba 中的一个组件。Sentinel 旨在解决分布式系统中的流量控制和熔断问题,帮助开发人员保护微服务应用免受系统负载过高和故障的影响。
Sentinel 主要有以下几个功能:
需要使用 Sentinel 的原因主要有以下几个:
流量控制是指对系统中的请求流量进行限制和管理,以确保系统在承受能力范围内正常运行。
常见的流量控制算法:
计数器算法是在一定时间间隔里,记录请求的次数,当请求次数超过该时间限制时,就把计数器清零,然后重新计算。当请求次数超过间隔内最大次数时,拒绝访问。
计数器算法的特点:实现简单,但存在 " 突刺现象"。
突刺现象是指,比如限流 QPS 为 100,算法的实现思路就是从一个请求进来开始计时,再接下来的 1s 内,每来一个请求,就把计数加1,如果累加的数字达到了 100,后续的请求就会被全部拒绝。等到 1 秒结束后,把计数恢复成0,重新开始计数。如果再单位时间 1 秒内的前 10 毫秒处理了 100 个请求,那么后面的 990 毫秒会拒绝所有的请求,我们把这种现象称为“突刺现象”。
漏桶算法的实现思路是,有一个固定容量的漏桶,水流(请求)可以按照任意速率进入到漏桶里,但漏桶总是以固定的速率匀速流出,当流入量过大的时候(超过桶的容量),则多余水流(请求)直接溢出。
漏桶算法提供了一种机制,通过它可以让突发流量被整形,以便为系统提供稳定的请求,比如 Sentinel 中流量整形(匀速排队功能)就是此算法实现的。
令牌按照固定的速率被放入到令牌桶中,桶中最多放 N个令牌(Token), 当桶装满时,新添加的令牌被丢弃或拒绝,当请求到达时,将从桶中删除 1 个令牌。令牌中的令牌不仅可以被移除,还可以往里添加,所以为了保证接口随时有数据通过,必须不停地往桶里加令牌。由此可见,往桶里加令牌地速度就决定了数据通过接口地速度,我们通过控制往令牌桶里加令牌地速度从而控制接口地流量
Sentinel 流量控制有以下几个角度:
流控效果介绍如下:
熔断是一种在分布式系统中处理故障和异常的策略。当某个服务或者接口发生故障或异常时,熔断机制会迅速将请求拒绝或者返回错误信息,而不是让请求一直等待或者重试,以保护系统免受故障的扩散影响。
熔断的原理是通过对服务的监控和评估,根据一定的规则来判断服务的健康状况。当服务的错误率或者响应时间超过设定的阈值时,熔断器会触发,并将后续的请求迅速拦截或者返回错误信息,而不是继续调用该服务,从而避免由于故障服务的长时间不可用或者响应过慢而导致整个系统的而延迟或者崩溃。
熔断机制的好处是能够快速失败并返回错误信息,避免资源的浪费和系统的连锁故障。当服务恢复正常时,熔断器会逐渐放行请求,验证服务的可用性,确保系统逐渐恢复正常运行。
Sentinel 熔断配置如下:
1. 资源:Sentinel 中被保护的对象,可以是方法、接口、URL等
2. 规则:对资源的访问策略,包括限流、熔断、热点等。规则包括以下这些
Sentinel 基本使用主要有以下4步:
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
- </dependency>
1.通过代码定义资源
通过代码的方式 SphU.entry("resourceName")来定义资源,具体实现代码如下:
- @RequestMapping("getname")
- public String getName(){
- try(Entry enty= SphU.entry("getname")){
- return "Name:"+new Random().nextInt(1000);
- } catch (BlockException e) {
- return "被限流了";
- }
- }
SphU 是 Sentinel Protection Hotspot Util 的缩写,Sentinel 热点保护工具类。
2. 通过注解方式定义资源
通过注解 @SentinelResource 也可以实现资源的定义,如下代码所示:
- @RequestMapping("/getnamebyid")
- @SentinelResource(value = "getnamebyid",blockHandler = "myblockHandler",fallback = "otherhandler")
- public String getNameById(Integer id){
- int num=new Random().nextInt(100);
- System.out.println(num);
- int i = num / 0;
- // throw new RuntimeException();
- return "ID: "+new Random().nextInt(1000);
- }
-
- public String myblockHandler(Integer id,BlockException blockException){
- return "被限流了";
- }
- public String otherhandler(Integer id,Throwable e){
- return "其他异常";
- }
注意事项
@SentinelResource 注解属性说明:
- value:资源名称,必填项
- entryType:资源调用的流量类型:入口流量(EntryType.IN)和出口流量(EntryType.OUT),注意系统规则只对 IN 生效。
- blockHandler/blockHandlerClass:限流和熔断时执行 BlockException 所对应的方法名。
- fallback/fallbackClass:非 BlockException 时,其他非限流、非熔断时异常对应的方法。
- exceptionsToIgnore:用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是原样抛出。
注:1.6.0 之前的版本 fallback 函数只针对熔断降级异常(DegradeException)进行处理,不能针对业务异常进行处理。
- @SpringBootApplication
- public class SentinelDemoApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(SentinelDemoApplication.class, args);
- initFlowRules();
- }
-
- public static void initFlowRules(){
- List<FlowRule> rules=new ArrayList<>();
- FlowRule rule=new FlowRule();
- rule.setResource("getnamebyid");
- rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
- rule.setCount(1);
- rule.setStrategy(RuleConstant.STRATEGY_DIRECT);
- rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
- rule.setClusterMode(false);
- rules.add(rule);
- FlowRuleManager.loadRules(rules);
- }
-
- }
-
- //热点参数流控规则(需要配合@SentinelResource 使用)
- public static void initFlowRules(){
- List<ParamFlowRule> rules = new ArrayList<>();
- List<ParamFlowItem> paramFlowItemList=new ArrayList<>();
-
- ParamFlowItem paramFlowItem1 = new ParamFlowItem();
- paramFlowItem1.setClassType(Integer.class.getName()); // 参数类型为 String
- paramFlowItem1.setCount(2);
- paramFlowItem1.setObject("id"); // 参数名为 id
- paramFlowItemList.add(paramFlowItem1);
-
- // 设置热点参数流控规则
- ParamFlowRule paramFlowRule = new ParamFlowRule("/user/getnamebyid") // 对应的资源名
- .setParamIdx(0) // 参数的索引,从0开始,如果方法参数中的第一个参数是id,则索引为0
- .setGrade(RuleConstant.FLOW_GRADE_QPS) // 参数限流的阈值类型,比如QPS
- .setCount(2) // 参数的阈值
- .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)
- .setParamFlowItemList(paramFlowItemList);
- rules.add(paramFlowRule);
- ParamFlowRuleManager.loadRules(rules);
- }
-
- @RequestMapping("/getnamebyid")
- @SentinelResource(value = "/user/getnamebyid")
- public String getNameById(Integer id) {
- // Thread.sleep(300);
- return "ID: "+new Random().nextInt(1000);
- }
-
- // 授权规则
- public static void initFlowRules(){
- List<AuthorityRule> rules=new ArrayList<>();
- AuthorityRule rule=new AuthorityRule();
- rule.setResource("/user/getnamebyid");
- rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
- rule.setLimitApp("qingkai,akai"); //设置白名单,只有"qingkai" 和 "akai" 可以访问
- rules.add(rule);
- AuthorityRuleManager.loadRules(rules);
- }
其中:
1. setStrategy:设置调用关系限流策略,包含的值有:
2. setControlBehavior:设置流控效果,包含的值有:
熔断降级功能的实现和限流功能类似,唯一的不同是定义规则上的不同。
熔断降级定义规则代码如下:
- //熔断规则
- public static void initDegradeRules(){
- List<DegradeRule> rules=new ArrayList<>();
- DegradeRule rule=new DegradeRule();
- rule.setResource("getnamebyid");
- //熔断降级策略,支持慢调用比例/异常比例/异常数策略
- rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
- //慢调用比例模式下为慢调用临界 RT,单位:ms(超出该值为慢调用);异常比例/异常数模式下为对应的阈值
- rule.setCount(10);
- //熔断时长,单位:s
- rule.setTimeWindow(5);
- //熔断触发的最小请求数,请求数小于该值时及时异常比率超出阈值也不会熔断
- rule.setMinRequestAmount(1);
- //统计时长,单位:ms
- rule.setStatIntervalMs(1000);
- //慢调用比例阈值,仅慢调用比例模式有效
- rule.setSlowRatioThreshold(0.5);
- rules.add(rule);
- DegradeRuleManager.loadRules(rules);
- }
- public String myblockHandler(Integer id,BlockException blockException){
- if(blockException instanceof FlowException){
- return "被限流了";
- }if(blockException instanceof DegradeException){
- return "被熔断了";
- }
- return "请求被限制了";
- }
配置项 | 默认值 | 描述 |
server.port | 8080 | 指定端口号 |
csp.sentinel.dashboard.server | localhost:8080 | 指定地址 |
sentinel.dashboard.auth.username | sentinel | Dashboard 登录账号 |
sentinel.dashboard.auth.password | sentinel | Dashboard 登录密码 |
server.servlet.session.timeout | 30分钟 | 登录Session 过期时间 配置为7200 表示 7200秒 配置为60m 表示 60 分钟 |
project.name | - | 指定程序的名称 |
在项目中加入Sentinel依赖:
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
- </dependency>
在项目中配置 Sentinel Dashboard 地址:
- spring:
- application:
- name: sentinel-service
- cloud:
- sentinel:
- transport:
- dashboard: localhost:1234
- client-ip: 127.0.0.1
- port: 8720
- heartbeat-interval-ms: 1000
- dashboard:sentinel 控制台地址
- client-ip:当前客户端 Ip,不设置自动选择一个 IP注册
- port:与sentinel 通讯的端口,如不设置,会从 8719 开始扫描,依次 +1,直到找到未被占用的接口。
- heartbeat-interval-ms:心跳发送周期,默认是10s
流控模式:
Token Server 时 Sentinel 用于集群流量控制的关键组件,它负责分发令牌并进行流量控制。当 Sentinel 的应用程序配置为集权限流模式时,它会向 Token Server 请求令牌,然后根据令牌情况来进行流量控制。如果 Token 不可用,可能是由于网络故障、Token Server 实例崩溃等原因,这时候无法从 Token Server 获取令牌。
- 当配置选项为“是”时:表示Token Server 不可用时,Sentinel 会自动切换为单机限流模式。在单机限流模式中,Sentinel 会从本地的规则进行流量控制,不再依赖 Token Server。这样可以保证即使 Token Server 不可用,也能够继续对流量进行限制。
- 当配置选项为“否”时:表示当 Token Server 不可用时,Sentinel 不会自动切换为单机限流模式,流量控制会被暂停,即无法进行限流,可能会导致服务负载过高。
热点规则使用注意事项:
- 热点规则只在控制台配置时不生效的,需要在代码中配合 @SentinelResource 一起使用才行
- 热点规则配置的参数必须存在,如果不存在配置了也是无效的。且前端传递的参数名和后端参数名要一致才有效
- 高级选项可以配置特定的参数的限流阈值
针对某个接口的调用服务做黑、白名单限制
- @Component
- public class CustomerRequestOriginParser implements RequestOriginParser {
- @Override
- public String parseOrigin(HttpServletRequest request) {
- String origin=request.getHeader("origin");
- if(!StringUtils.hasLength(origin)){
- origin="blank";
- }
- return origin;
- }
- }
此时只有请求头中含有 "origin":"qingkai" 的请求才可以访问。
对整个项目做限流保护,设置如下:
- @RequestMapping("/getnamebyid")
- @SentinelResource(value = "getnamebyid",blockHandler = "myblockHandler")
- public String getNameById(Integer id) throws InterruptedException {
- // Thread.sleep(300);
- return "ID: "+new Random().nextInt(1000);
- }
-
- public String myblockHandler(Integer id,BlockException blockException){
- if(blockException instanceof FlowException){
- return "被限流了";
- }if(blockException instanceof DegradeException){
- return "被熔断了";
- }
- return "请求被限制了";
- }
- @Component
- public class globalException implements BlockExceptionHandler {
-
- @Override
- public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
- String msg="未知异常";
- int code= HttpStatus.TOO_MANY_REQUESTS.value();
- if(e instanceof FlowException){
- msg="您被限流了";
- }else if (e instanceof DegradeException){
- msg="您被熔断了";
- }else if(e instanceof AuthorityException){
- msg="没有访问权限";
- }else if(e instanceof ParamFlowException){
- msg="请求热点参数被限流";
- }
- httpServletResponse.setContentType("application/json;charset=utf8");
- httpServletResponse.setStatus(code);
- httpServletResponse.getWriter().println("{\"msg\":"+msg+",\"code\":"+code+",\"data\":[]");
- }
- }
- @RestControllerAdvice
- public class SystemException{
- @ExceptionHandler(BlockException.class)
- public String handlerException(Exception e){
- if(e instanceof ParamFlowException){
- return "请求出发了热点限流了!!!";
- }
- return "限流";
- }
- }
Sentinel 使用 Dashboard 可以动态修改流控规则,但因为默认时存储在内存中的,所以重启之后数据就丢失了,因此我们可以配合Nacos、ZooKeeeper、Redis 等存储数据源。
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
- </dependency>
- <dependency>
- <groupId>com.alibaba.csp</groupId>
- <artifactId>sentinel-datasource-nacos</artifactId>
- </dependency>
- spring:
- application:
- name: sentinel-service
- cloud:
- sentinel:
- transport:
- dashboard: localhost:18080
- datasource:
- ds:
- nacos:
- server-addr: localhost:8848
- username: nacos
- password: nacos
- data-id: ${spring.application.name}-flow-rules
- group-id: DEFAULT_GROUP
- data-type: json
- rule-type: flow #限流
- ds2:
- nacos:
- server-addr: localhost:8848
- username: nacos
- password: nacos
- data-id: ${spring.application.name}-degrade-rules
- group-id: DEFAULT_GROUP
- data-type: json
- rule-type: degrade #熔断
- ds3:
- nacos:
- server-addr: localhost:8848
- username: nacos
- password: nacos
- data-id: ${spring.application.name}-paramflow-rules
- group-id: DEFAULT_GROUP
- data-type: json
- rule-type: param_flow #热点限流
- ds4:
- nacos:
- server-addr: localhost:8848
- username: nacos
- password: nacos
- data-id: ${spring.application.name}-paramflow-rules
- group-id: DEFAULT_GROUP
- data-type: json
- rule-type: authority #黑白名单
- ds:数据源名字,可以随便起
- nacos:表示 Nacos 数据源
- server-addr:Nacos 服务器地址
- password:Nacos 密码
- data-id:Nacos 新建配置的 Data ID
- group-id:分组 ID
- data-type:数据格式
- rule-type:规则类型
- [
- {
- "resource":"/user/getnamebyid",
- "limitApp":"default",
- "grade":1,
- "count":1,
- "strategy":0,
- "controlBehavior":0,
- "clusterMode":false
- }
- ]
属性说明:
字段 | 说明 | 默认值 |
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS(1)或线程数(0)模式,RuleConstant类中查看值所代表的含义。 | QPS模式 |
limitApp | 流控针对的调用来源 | default,代表不区分调用来源 |
Strategy | 调用关系限流策略:直接(0)、关联(1)、链路(2) | 直接 |
controlBehavior | 流控效果:直接拒绝(0)、排队等待(2)、慢启动(1)、慢启动+排队(3) | 直接拒绝 |
clusterMode | 是否设置为集群模式 |
- [
- {
- "resource":"/user/getname",
- "grade":0,
- "count":10,
- "timeWindow":3,
- "minRequestAmount":1,
- "statIntervalMs":1000,
- "slowRatioThreshold":0.5
- }
- ]
Field | 说明 |
grade | 熔断策略,支持慢调用比例(0)、异常比例(1)、异常数策略(2) |
count | 满调用比例模式下调用临界RT(超出改时间为满调用);异常比例/异常数模式下为对应的阈值 |
timeWindow | 熔断时长,单位为秒 |
minRequestAmount | 熔断触发的最小请求数,请求数小于该值时即使异常比率超过阈值也不会熔断 |
statIntervalMs | 统计时长,单位为ms |
slowRatioThreshold | 满调用比例阈值,仅满调用比例模式有效 |
- [
- {
- "resource":"/user/getnamebyid",
- "grade":1,
- "count":2,
- "paramIdx":0,
- "controlBehavior":0,
- "paramFlowItemList":[
- {
- "classType":"Integer", #参数值类型
- "count":1,
- "object":"id" #参数名称
- }
- ]
- }
- ]
- @RequestMapping("/getnamebyid")
- @SentinelResource(value = "/user/getnamebyid")
- public String getNameById(Integer id) {
- // Thread.sleep(300);
- return "ID: "+new Random().nextInt(1000);
- }
- [
- {
- "resource":"/user/getnamebyid",
- "strategy":0, #0为设置白名单,1为设置黑名单
- "limitApp":"qingkai,akai" #设置白名单,只有"qingkai" 和 "akai" 可以访问
- }
- ]
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
- </dependency>
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-gateway</artifactId>
- </dependency>
在 application 中设置 sentinel 控制台的地址,以及限流之后的影响信息:
- spring:
- sentinel:
- transport:
- dashboard: localhost:18080
- scg: #配置限流之后响应内容
- fallback:
- #两种方式,一种时 response 返回文字提示信息,
- #另一种时 redirect 重定向,配置 redirect 要配置对应调转的uri
- mode: response
- response-status: 200
- response-body: '{"code"429,"message":"被限流了“}'
重定向配置:
- spring:
- cloud:
- sentinel:
- transport:
- dashboard: localhost:18080
- scg: #配置限流之后响应内容
- fallback:
- #两种方式,一种时 response 返回文字提示信息,
- #另一种时 redirect 重定向,配置 redirect 要配置对应调转的uri
- mode: redirect
- redirect: https://www.baidu.com
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。