Alibaba Sentinel功能入门与配置持久化

Alibaba Sentinel功能入门与配置持久化

文章目录

1.准备工作

  • sentinel dashboard jar包下载,这里结合项目SpringCloud版本,选用官网推荐的1.8.1

  • 适当版本的JDK(可参考dashboard包内文档建议)

  • 一个微服务项目(我这里使用的是之前自己写的springcloud+nacos项目,源代码参见SpringCloudAlibaba: SpringCloudAlibaba练习项目 (gitee.com),思路参见https://blog.csdn.net/qq_61603262/article/details/127345832),具体版本如下:

            <druid.version>1.2.13</druid.version>
            <mysql.version>8.0.30</mysql.version>
            <spring-boot-starter-data-jdbc.version>2.7.4</spring-boot-starter-data-jdbc.version>
            <spring-boot-starter-jdbc.version>2.7.4</spring-boot-starter-jdbc.version>
            <mybatis.version>2.1.4</mybatis.version>
            <springdoc.version>1.6.11</springdoc.version>
    

2.整合与启动

2.1项目引入依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
    <version>2.2.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.2.7.RELEASE</version>
</dependency>

2.2项目yaml配置

spring: 
  cloud:
    sentinel:
      enabled: true
      transport:
        dashboard: localhost:8080 #这里配置的是dashboard对应的端口号,项目与jar包运行端口保持一致即可

2.3控制台配置项

控制台的一些特性可以通过配置项来进行配置,配置项主要有两个来源:System.getProperty()System.getenv(),同时存在时后者可以覆盖前者。

通过环境变量进行配置时,因为不支持 . 所以需要将其更换为 _

配置项类型默认值最小值描述
auth.enabledbooleantrue-是否开启登录鉴权,仅用于日常测试,生产上不建议关闭
sentinel.dashboard.auth.usernameStringsentinel-登录控制台的用户名,默认为 sentinel
sentinel.dashboard.auth.passwordStringsentinel-登录控制台的密码,默认为 sentinel
sentinel.dashboard.app.hideAppNoMachineMillisInteger060000是否隐藏无健康节点的应用,距离最近一次主机心跳时间的毫秒数,默认关闭
sentinel.dashboard.removeAppNoMachineMillisInteger0120000是否自动删除无健康节点的应用,距离最近一次其下节点的心跳时间毫秒数,默认关闭
sentinel.dashboard.unhealthyMachineMillisInteger6000030000主机失联判定,不可关闭
sentinel.dashboard.autoRemoveMachineMillisInteger0300000距离最近心跳时间超过指定时间是否自动删除失联节点,默认关闭
sentinel.dashboard.unhealthyMachineMillisInteger6000030000主机失联判定,不可关闭
server.servlet.session.cookie.nameStringsentinel_dashboard_cookie-控制台应用的 cookie 名称,可单独设置避免同一域名下 cookie 名冲突

配置示例:

  • 命令行方式:
java -Dsentinel.dashboard.app.hideAppNoMachineMillis=60000
  • Java 方式:
System.setProperty("sentinel.dashboard.app.hideAppNoMachineMillis", "60000");
  • 环境变量方式:
 sentinel_dashboard_app_hideAppNoMachineMillis=60000

2.4启动项目与sentinel-dashboard.jar

java -jar sentinel-dashboard.jar

2.5测试

访问项目中任意服务,在dashboard能够看到该项目的spring.application.name,打开选项卡后可以看到相关访问统计。

3.可能出现的异常

3.1.The Bean Validation API is on the classpath but no implementation could be found

***************************
APPLICATION FAILED TO START
***************************

Description:

The Bean Validation API is on the classpath but no implementation could be found

Action:

Add an implementation, such as Hibernate Validator, to the classpath

解决方案:

引入依赖

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>8.0.0.Final</version>
</dependency>

如不可行,尝试引入springboot自带的validation:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

3.2.sentinel-dashboard.jar启动:java.lang.IllegalStateException: Cannot load configuration class:com.alibaba.csp.sentinel.dashboard.DashboardApplication

启动时换用下述命令(加入启动参数):

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.net.util=ALL-UNNAMED -jar sentinel-dashboard-1.8.1.jar

4.具体使用配置

4.1限流规则

  • 普通限流

  • 关联限流:当对A访问的QPS/线程数达到阈值时,对B限流。使用时需要配置B的限流规则。即给谁限流就配置谁的限流规则(配置访问优先级较低的服务)。

  • 链路限流:限制来自不同路径对同一服务的访问。需要设置web-context-unify: false才会生效。限制那个路径的QPS就配置哪个路径。

  • 热点参数限流:根据请求携带参数限流。

    注意:该配置只对添加了@SentinelResource注解的资源生效

4.2限流结果

  • 快速失败:抛出FlowException,返回429错误

  • warm up:同快速失败,但在抛出异常的同时,阈值会逐渐增大(默认从最大值的三分之一开始)。常用于服务冷启动。

    ​ 参数:预热时长

  • 排队等待:接到超过单机阈值的请求不会立即抛出异常,把请求放进队列延迟进行直至超时或被执行。

5.服务降级、隔离与熔断

5.1consumer端Feign整合Sentinel

5.1.1引入依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
    <version>2.2.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.2.7.RELEASE</version>
</dependency>
5.1.2yaml添加配置
feign:
  sentinel:
    enabled: true
5.1.3编写失败后的返回方法FallbackFactory

这里要注意的是,FallbackFactory有两个类,即OpenFeign的和Hystrix的,这里我们要继承的是Hystrix的。

package com.zjy.consumer9000.config;

import com.zjy.consumer9000.service.DeptFeignService;
import feign.hystrix.FallbackFactory;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DeptFallbackFactory implements FallbackFactory<DeptFeignService> {
    @Override
    public DeptFeignService create(Throwable cause) {
        return new DeptFeignService() {
            @Override
            public List<Map> getAll() {
                Map map=new HashMap();
                map.put("msg","unknown exception");
                List<Map> list=new ArrayList<>();
                list.add(map);
                return list;
            }

            @Override
            public Map queryById(Integer departmentId) {
                Map map=new HashMap();
                map.put("msg","not found");
                return map;
            }

            @Override
            public List<Map> queryByName(String departmentName) {
                Map map=new HashMap();
                map.put("msg","there's no such a department");
                List<Map> list=new ArrayList<>();
                list.add(map);
                return list;
            }
        };
    }
}

注册bean

package com.zjy.consumer9000.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FallbackFactoryConfig {
    @Bean
    public DeptFallbackFactory getDeptFallbackFactory(){
        return new DeptFallbackFactory();
    }
}

在FeignClient加入注解以激活fallbackFactory

@FeignClient(value = "dataProvider8000",fallbackFactory = DeptFallbackFactory.class)
5.1.4修改provider端的Controller
@Operation(summary = "通过id查找部门")
@GetMapping("/query/id/{departmentId}")
@SentinelResource
public Department queryById(@PathVariable Integer departmentId){
    Department dept=departmentService.getDepartmentById(departmentId);
    if(dept.getDepartmentName()==null)
        throw new NullPointerException("there's no such a dept!"){
    };
    return dept;
}

这里笔者只改了其中一个方法来验证,其他类似

5.2线程隔离

5.2.1信号量隔离(默认)

信号量隔离的其实很简单,就是在Constumer和Provider之间加上一个的[消息队列,在指定的时间内只允许指定的请求量能够访问到Provider,如果请求超出,则返回托底数据。

5.2.2线程池隔离

线程池是通过限制线程数,来对资源使用进行限制,而不是隔离。限制资源使用,防止系统过载。

使用线程池的优点
  1. 使用线程池隔离可以完全隔离依赖的服务,请求线程可以快速找回。
  2. 当线程池出现问题时,线程池隔离是独立的,不会影响其他服务的接口。
  3. 当失败的服务变得可用时,线程池将清理并立即修复,而不需要一个漫长的等待。
  4. 独立的线程池提高了并发性。
使用线程池的缺点

线程池的主要缺点是增加了cpu的开销,每个命令涉及到排队,调度和上下文切换都是在一个独立的线程上运行的。

5.3线程熔断

5.3.1慢调用比例 (SLOW_REQUEST_RATIO)

选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
Sentinel默认统计的RT上限是4900ms,超出此阈值的都会算作4900ms,若需要变更此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx来配置

5.3.2异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO)

当资源的每秒请求量 >= N(可配置),并且每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

5.3.3异常数 (DEGRADE_GRADE_EXCEPTION_COUNT)

当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。

6.统一处理异常

6.1方法一:实现sentinel内置接口

sentinel正常工作情况下抛出的异常:BlockException(FlowException、ParamFlowException、DegradeException、AuthorityException、SystemBlockException )。相应的,sentinel提供了处理该异常的接口BlockExceptionHandler。我们只需要实现该接口就可以完成对该异常的自定义抛出。代码如下

package com.zjy.dataprovider8000.config;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class SentinelBlockHandler implements BlockExceptionHandler {


    @Override
    public void handle(HttpServletRequest httpServletRequest,
                       HttpServletResponse httpServletResponse,
                       BlockException e) throws Exception {
        String msg="未知错误";
        int status=429;
        if (e instanceof FlowException) msg = "限流异常";
        if (e instanceof DegradeException)msg = "服务降级";
        if (e instanceof ParamFlowException)msg = "热点限流";

        httpServletResponse.getWriter().print(msg);
        httpServletResponse.setContentType("application/json;charset=utf-8");
        httpServletResponse.setStatus(status);
        System.err.println(msg);
    }
}

6.2方法二:通过SpringCloud自带注解

思路
  1. 新建一个ExceptionHandler类,用@RestControllerAdvice(basePackages = “com.xxx.controller”)注解指明要处理的包
  2. 在方法上用@ExceptionHandler(value= Exception.class)指明要捕捉的异常类
package com.zjy.dataprovider8000.config;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;


@RestControllerAdvice(basePackages = "com.zjy.dataprovider8000.controller")
public class SentinelExceptionHandler  {
    @ResponseBody
    @ExceptionHandler(value= Exception.class)
    public Map handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        String msg = "未知异常";
        int status = 429;

        if (e instanceof FlowException) {
            msg = "请求被限流了";
        } else if (e instanceof ParamFlowException) {
            msg = "请求被热点参数限流";
        } else if (e instanceof DegradeException) {
            msg = "请求被降级了";
        } else if (e instanceof AuthorityException) {
            msg = "没有权限访问";
            status = 401;
        }

        response.setContentType("application/json;charset=utf-8");
        response.setStatus(status);
        //response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}");
        HashMap map=new HashMap();
        map.put("msg",msg);
        return map;
    }
}

这里我们没有用网关来限制权限,之后将全部交由安全框架解决

7.生产环境下配置持久化(整合nacos的push模式)

7.1规则属性一览

流量规则(FlowRule)
Field说明默认值
resource资源名,资源名是限流规则的作用对象
count限流阈值
grade限流阈值类型,QPS 模式(1)或并发线程数模式(0)QPS 模式
limitApp流控针对的调用来源default,代表不区分调用来源
strategy调用关系限流策略:直接、链路、关联根据资源本身(直接)
controlBehavior流控效果(直接拒绝/WarmUp/匀速+排队等待),不支持按调用关系限流直接拒绝
clusterMode是否集群限流
熔断降级规则(DegradeRule)
Field说明默认值
resource资源名,即规则的作用对象
grade熔断策略,支持慢调用比例/异常比例/异常数策略慢调用比例
count慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值
timeWindow熔断时长,单位为 s
minRequestAmount熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入)5
statIntervalMs统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入)1000 ms
slowRatioThreshold慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)
热点参数规则(ParamFlowRule
更多操作Field说明默认值
resource资源名,必填
count限流阈值,必填
grade限流模式QPS 模式
durationInSec统计窗口时间长度(单位为秒),1.6.0 版本开始支持1s
controlBehavior流控效果(支持快速失败和匀速排队模式),1.6.0 版本开始支持快速失败
maxQueueingTimeMs最大排队等待时长(仅在匀速排队模式生效),1.6.0 版本开始支持0ms
paramIdx热点参数的索引,必填,对应 SphU.entry(xxx, args) 中的参数索引位置
paramFlowItemList参数例外项,可以针对指定的参数值单独设置限流阈值,不受前面 count 阈值的限制。仅支持基本类型和字符串类型
clusterMode是否是集群参数流控规则false
clusterConfig集群流控相关配置
系统保护规则 (SystemRule)

Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统规则包含下面几个重要的属性:

Field说明默认值
highestSystemLoadload1 触发值,用于触发自适应控制阶段-1 (不生效)
avgRt所有入口流量的平均响应时间-1 (不生效)
maxThread入口流量的最大并发数-1 (不生效)
qps所有入口资源的 QPS-1 (不生效)
highestCpuUsage当前系统的 CPU 使用率(0.0-1.0)-1 (不生效)

7.2硬编码实现

流量规则(FlowRule)
private void initFlowQpsRule() {
    List<FlowRule> rules = new ArrayList<>();
    FlowRule rule = new FlowRule(resourceName);
    // set limit qps to 20
    rule.setCount(20);
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    rule.setLimitApp("default");
    rules.add(rule);
    FlowRuleManager.loadRules(rules);
}
熔断降级规则 (DegradeRule)
private void initDegradeRule() {
    List<DegradeRule> rules = new ArrayList<>();
    DegradeRule rule = new DegradeRule();
    rule.setResource(KEY);
    // set threshold RT, 10 ms
    rule.setCount(10);
    rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
    rule.setTimeWindow(10);
    rules.add(rule);
    DegradeRuleManager.loadRules(rules);
}
热点参数规则(ParamFlowRule
private void initDParamFlowRule() {
   ParamFlowRule rule = new ParamFlowRule(resourceName)
    .setParamIdx(0)
    .setCount(5);
// 针对 int 类型的参数 PARAM_B,单独设置限流 QPS 阈值为 10,而不是全局的阈值 5.
ParamFlowItem item = new ParamFlowItem().setObject(String.valueOf(PARAM_B))
    .setClassType(int.class.getName())
    .setCount(10);
rule.setParamFlowItemList(Collections.singletonList(item));
ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
}
系统保护规则 (SystemRule)
private void initSystemRule() {
    List<SystemRule> rules = new ArrayList<>();
    SystemRule rule = new SystemRule();
    rule.setHighestSystemLoad(10);
    rules.add(rule);
    SystemRuleManager.loadRules(rules);
}

7.3json实现

引入依赖
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
yaml
spring:
  cloud:
    sentinel:
      datasource:
        # 名称随意
        flow:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-flow-rules
            groupId: SENTINEL_GROUP
            # 规则类型,取值见:
            # org.springframework.cloud.alibaba.sentinel.datasource.RuleType
            rule-type: flow
        degrade:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-degrade-rules
            groupId: SENTINEL_GROUP
            rule-type: degrade
        system:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-system-rules
            groupId: SENTINEL_GROUP
            rule-type: system
        authority:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-authority-rules
            groupId: SENTINEL_GROUP
            rule-type: authority
        param-flow:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-param-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: param-flow

流量规则(FlowRule)
[
  {
    // 资源名
    "resource": "/test",
    // 针对来源,若为 default 则不区分调用来源
    "limitApp": "default",
    // 限流阈值类型(1:QPS;0:并发线程数)
    "grade": 1,
    // 阈值
    "count": 1,
    // 是否是集群模式
    "clusterMode": false,
    // 流控效果(0:快速失败;1:Warm Up(预热模式);2:排队等待)
    "controlBehavior": 0,
    // 流控模式(0:直接;1:关联;2:链路)
    "strategy": 0,
    // 预热时间(秒,预热模式需要此参数)
    "warmUpPeriodSec": 10,
    // 超时时间(排队等待模式需要此参数)
    "maxQueueingTimeMs": 500,
    // 关联资源、入口资源(关联、链路模式)
    "refResource": "rrr"
  }
]

熔断降级规则 (DegradeRule)
[
  {
  	// 资源名
    "resource": "/test1",
    "limitApp": "default",
    // 熔断策略(0:慢调用比例,1:异常比率,2:异常计数)
    "grade": 0,
    // 最大RT、比例阈值、异常数
    "count": 200,
    // 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)
    "slowRatioThreshold": 0.2,
    // 最小请求数
    "minRequestAmount": 5,
    // 当单位统计时长(类中默认1000)
    "statIntervalMs": 1000,
    // 熔断时长
    "timeWindow": 10
  }
]

热点参数规则(ParamFlowRule
[
  {
  	// 资源名
    "resource": "/test1",
    // 限流模式(QPS 模式,不可更改)
    "grade": 1,
    // 参数索引
    "paramIdx": 0,
    // 单机阈值
    "count": 13,
    // 统计窗口时长
    "durationInSec": 6,
    // 是否集群 默认false
    "clusterMode": 默认false,
    // 
    "burstCount": 0,
    // 集群模式配置
    "clusterConfig": {
      // 
      "fallbackToLocalWhenFail": true,
   	  // 
      "flowId": 2,
      // 
      "sampleCount": 10,
      // 
      "thresholdType": 0,
      // 
      "windowIntervalMs": 1000
    },
    // 流控效果(支持快速失败和匀速排队模式)
    "controlBehavior": 0,
    // 
    "limitApp": "default",
    // 
    "maxQueueingTimeMs": 0,
    // 高级选项
    "paramFlowItemList": [
      {
      	// 参数类型
        "classType": "int",
      	// 限流阈值
        "count": 222,
      	// 参数值
        "object": "2"
      }
    ]
  }
]
系统保护规则 (SystemRule)
[
  {
  	// RT
    "avgRt": 1,
    // CPU 使用率
    "highestCpuUsage": -1,
    // LOAD
    "highestSystemLoad": -1,
    // 线程数
    "maxThread": -1,
    // 入口 QPS
    "qps": -1
  }
]

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值