SpringCloud Alibaba-Sentinel

news2024/11/26 22:36:14

SpringCloud Alibaba-Sentinel

  • 1. Sentinel核心库
    • 1.1 Sentinel介绍
    • 1.2 Sentinel核心功能
      • 1.2.1 流量控制
      • 1.2.2 熔断降级
  • 2 Sentinel 限流熔断降级
    • 2.1 @SentinelResource定义资源
      • 2.1.1 blockHandler/blockHandlerClass
      • 2.1.2 fallback/fallbackClass
      • 2.1.3 defaultFallback
    • 2.2 Sentinel的规则
      • 2.2.1 流量控制规则 (FlowRule)
        • 2.2.1.1 QPS流量控制
        • 2.2.1.2 线程数流量控制
      • 2.2.2 熔断降级规则 (DegradeRule)
      • 2.2.3 系统保护规则 (SystemRule)
      • 2.2.4 访问控制规则 (AuthorityRule)
      • 2.2.5 热点规则 (ParamFlowRule)
    • 2.3 OpenFeign支持
      • 2.3.1 fallback
      • 2.3.2 fallbackFactory
  • 3 Sentinel集成Gateway
    • 3.1 Sentinel对网关支持
    • 3.2 GateWay集成Sentinel
  • 4 Sentinel控制台
    • 4.1 Sentinel控制台安装
    • 4.2 接入控制台
    • 4.3 可视化管理
      • 4.3.1 实时监控
      • 4.3.2 流控规则
      • 4.3.3 降级规则
      • 4.3.4 热点数据

1. Sentinel核心库

Sentinel 主页

1.1 Sentinel介绍

在这里插入图片描述
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。

  1. Sentinel核心组件
  • 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 7 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持
  • 控制台(Dashboard):控制台主要负责管理推送规则、监控、集群限流分配管理、机器发现等
  1. 同组件功能对比

参看 官方网站

对比内容SentinelHystrix
隔离策略信号量隔离线程池隔离/信号量隔离
熔断降级策略基于响应时间或失败比率基于失败比率
实时指标实现滑动窗口滑动窗口(基于 RxJava)
规则配置支持多种数据源支持多种数据源
扩展性多个扩展点插件的形式
基于注解的支持支持支持
限流信号量隔离基于 QPS,支持基于调用关系的限流不支持
流量整形支持慢启动、匀速器模式不支持
系统负载保护支持不支持
控制台开箱即用,可配置规则、查看秒级监控、机器发现等不完善
常见框架的适配Servlet、Spring Cloud、Dubbo、gRPC 等Servlet、Spring Cloud Netflix
多语言支持Java/Go/C++Java
  1. Sentinel基本概念
  • 资源
    资源是 Sentinel 的关键概念。资源,可以是一个方法、一段代码、由应用提供的接口,或者由应用调用其它应用的接口
  • 规则
    围绕资源的实时状态设定的规则,包括流量控制规则、熔断降级规则以及系统保护规则、自定义规则
  • 降级
    在流量剧增的情况下,为保证系统能够正常运行,根据资源的实时状态、访问流量以及系统负载有策略的拒绝掉一部分流量
  • 熔断
    一个轻轻过来, 可能服务调用的链路长, 在某个服务发生问题时可能会造成服务雪崩 如果触发熔断机制,则可以保证后续的请求不会继续发送到目标服务上
    降级和熔断的区别是, 降级为可用, 熔断为不可用

1.2 Sentinel核心功能

1.2.1 流量控制

流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示:
在这里插入图片描述

流量控制有以下几个角度:

  • 资源的调用关系,例如资源的调用链路,资源和资源之间的关系
  • 运行指标,例如 QPS、线程池、系统负载等
  • 控制的效果,例如直接限流、冷启动、排队等

1.2.2 熔断降级

除了流量控制以外,及时对调用链路中的不稳定因素进行熔断也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,可能会导致请求发生堆积,进而导致级联错误
在这里插入图片描述
Sentinel 和 Hystrix 的原则是一致的: 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障

Sentinel熔断降级设计:
Hystrix 通过 线程池隔离 的方式,来对依赖(在 Sentinel 的概念中对应 资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线程数目过多),还需要预先给各个资源做线程池大小的分配

Sentinel熔断降级设计:

  1. 并发线程数限制:和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要用户预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求
  2. 响应时间降级:除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。

系统自适应保护:
Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。

针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

2 Sentinel 限流熔断降级

Sentinel 可以简单的分为 Sentinel 核心库和 Dashboard。核心库不依赖 Dashboard,但是结合 Dashboard 可以取得最好的效果
在这里插入图片描述
以下内容根据上面服务架构进行熔断限流操作

使用Spring Cloud Alibaba Sentinel 使用手册
首先需要引入spring-cloud-starter-alibaba-sentinel依赖,并使用@SentinelResource标识资源
在hailtaxi-driver工程中引入spring-cloud-starter-alibaba-sentinel依赖,依赖如下:

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

2.1 @SentinelResource定义资源

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性: 具体参考

value资源名称,必需项(不能为空)
blockHandler / blockHandlerClassblockHandler 对应处理 BlockException 的函数名称,可选项
  • blockHandler 函数访问范围需要是 public;
  • 返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException
  • blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析
fallback / fallbackClassfallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
  • 返回值类型必须与原函数返回值类型一致
  • 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常
  • allback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析
defaultFallback(1.6.0 开始)默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
  • 返回值类型必须与原函数返回值类型一致
  • 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常
  • defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析
exceptionsToIgnore(1.6.0 开始)用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出
entryTypeentry 类型,可选项(默认为 EntryType.OUT)

2.1.1 blockHandler/blockHandlerClass

制造SystemBlockException异常

@SentinelResource(value = "info",blockHandler = "blockExHandler")
@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id) throws BlockException {
    log.info("当前服务占用的端口为:{}",port);
    Driver driver = driverService.findById(id);
    if (driver==null) {
        //throw new RuntimeException("司机id="+id+"不存在");
        throw new SystemBlockException("info", "司机id="+id+"不存在",null); // 抛出BlockException
    }
    return driver;
}

/**
 * info资源出现BlockException后的降级处理
 */
public Driver blockExHandler(String id,BlockException e) {
    Driver driver = new Driver();
    driver.setId(id);
    driver.setName("系统繁忙,稍后再试");
    return driver;
}

指定blockHandlerClass
指定的class必须要是静态方法

@SentinelResource(value = "info",blockHandler = "blockExHandler",blockHandlerClass = "xxx.xxx.Xxxx")

注意:这种方式只能拦截BlockException异常, 局限性比较高

2.1.2 fallback/fallbackClass

如果希望抛出任何异常都能处理,都能调用默认处理方法,而并非只是BlockException异常才调用,此时可以使用@SentinelResource的fallback属性,代码如下:

@SentinelResource(value = "info"/*,blockHandler = "blockExHandler"*/,fallback = "exHandler")
@RequestMapping(value = "/info/{id}")
public Driver info(@PathVariable(value = "id")String id) throws BlockException {
    log.info("当前服务占用的端口为:{}",port);
    Driver driver = driverService.findById(id);
    if (driver==null) {
        throw new RuntimeException("司机id="+id+"不存在");
        // throw new SystemBlockException("info", "司机id="+id+"不存在",null); // 抛出BlockException
    }
    return driver;
}
/**
 * info资源出现任何类型异常后的降级处理
 * 方法参数可以添加一个Throwable 类型的参数,也可不添加
 */
public Driver exHandler(String id,Throwable e) {
    Driver driver = new Driver();
    driver.setId(id);
    driver.setName("系统繁忙,稍后再试");
    return driver;
}

指定fallbackClass
指定的class必须要是静态方法

@SentinelResource(value = "info",fallback ="exHandler" ,fallbackClass = "xx.xxx.xxx.xx.Xxx")

2.1.3 defaultFallback

上面无论是blockHandler还是fallback,每个方法发生异常,都要为方法独立创建一个处理异常的方法,效率非常低,可以使用@SentinelResource注解的defaultFallback属性,为一个类指定一个全局的处理错误的方法,代码如下:

@RestController
@RequestMapping(value = "/driver")
@Slf4j
@RefreshScope
@SentinelResource(defaultFallback = "defaultExHandler")
public class DriverController {
    @Autowired
    private DriverService driverService;

    public Driver defaultExHandler(Throwable e) {
        Driver driver = new Driver();
        driver.setName("系统繁忙,稍后再试");
        return driver;
    }

    /****
     * 司机信息
     */
    //@SentinelResource(value = "info"/*,blockHandler = "blockExHandler"*/,fallback = "exHandler")
    @SentinelResource("info")
    @RequestMapping(value = "/info/{id}")
    public Driver info(@PathVariable(value = "id")String id) throws BlockException {
        log.info("当前服务占用的端口为:{}",port);
        Driver driver = driverService.findById(id);
        if (driver==null) {
            throw new RuntimeException("司机id="+id+"不存在");
            // throw new SystemBlockException("info", "司机id="+id+"不存在",null); // 抛出BlockException
        }
        return driver;
    }

两个字: 也很麻烦
不过一般都不会这么写, 通常会和OpenFeign的FallbackFactory结合使用

2.2 Sentinel的规则

参考官网
Sentinel 的所有规则都可以在内存态中动态地查询及修改,修改之后立即生效。同时 Sentinel 也提供相关 API,供用户来定制自己的规则策略
Sentinel 支持以下几种规则:流量控制规则、熔断降级规则、系统保护规则、来源访问控制规则热点参数规则

2.2.1 流量控制规则 (FlowRule)

参看 链接1、链接2
流量规则的定义,重要属性如下:

Field说明默认值
resource资源名,资源名是限流规则的作用对象
count限流阈值
grade限流阈值类型,QPS 模式(1)或并发线程数模式(0)QPS 模式
limitApp流控针对的调用来源default,代表不区分调用来源
strategy调用关系限流策略:直接、链路、关联根据资源本身(直接)
controlBehavior流控效果(直接拒绝/WarmUp/匀速+排队等待),不支持按调用关系限流直接拒绝
clusterMode是否集群限流

同一个资源可以同时有多个限流规则,检查规则时会依次检查

strategy限流策略说明:

  1. 直接: 资源达到限流条件时,直接限流
  2. 关联: A资源关联B资源,当关联的B资源达到阈值限流时,A资源也会被限流
  3. 链路: 对于某资源C,有两个入口,从资源A->C,从资源B->C, 通过指定入口资源可以达到只记录从该入口进来的流量(指定资源从入口资源进来的流量,如果达到阈值,就可以对其限流)

controlBehavior流控说明:

  1. 直接拒绝: 请求直接失败
  2. WarmUp: 当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮
  3. 排队等待: 排队处理请求

2.2.1.1 QPS流量控制

Application初始化完成之后加载规则

/***
 * 初始化规则
 */
@PostConstruct
private void initFlowRule() {
    //规则集合
    List<FlowRule> rules = new ArrayList<FlowRule>();
    //定义一个规则
    FlowRule rule = new FlowRule("info");
    // 设置阈值
    rule.setCount(2);
    //设置限流阈值类型
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    //default,代表不区分调用来源
    rule.setLimitApp("default");
    //设置流控效果 当QPS超过任意规则的阈值后,新的请求就会被立即拒绝
    rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
    //将定义的规则添加到集合中
    rules.add(rule);
    //加载规则
    FlowRuleManager.loadRules(rules);
}

2.2.1.2 线程数流量控制

修改限流阈值类型,代码如下:

@PostConstruct
public void initFlowRule() {
    //规则集合
    List<FlowRule> rules = new ArrayList<>();
    // 定义一个规则
    FlowRule rule = new FlowRule("info");
    // 设置基流量控制 的类型
    rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);//默认是qps
    //设置流量阈值
    rule.setCount(2);
    // 将 规则添加到 集合中
    rules.add(rule);
    // 加载规则
    FlowRuleManager.loadRules(rules);
}

此时再来访问http://localhost:18081/driver/info/1 无论怎么访问都不会出现降级现象,但是如果用Jmeter模拟多个线程,会执行降级方法,效果如下:
在这里插入图片描述

2.2.2 熔断降级规则 (DegradeRule)

参看 链接1、链接2
熔断降级规则包含下面几个重要的属性:

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 引入)

同一个资源可以同时有多个降级规则
可以通过调用 DegradeRuleManager.loadRules() 方法来用硬编码的方式定义熔断规则

@PostConstruct
public void initDegradeRule() {
    List<DegradeRule> rules = new ArrayList<>();
    DegradeRule rule = new DegradeRule();
    // 设置资源名称
    rule.setResource("info");
    /**
         * 设置熔断策略
         * DEGRADE_GRADE_RT:平均响应时间
         * DEGRADE_GRADE_EXCEPTION_RATIO:异常比例数量
         * DEGRADE_GRADE_EXCEPTION_COUNT:异常数
         */
    rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
    //设置阈值
    rule.setCount(2);
    //设置 熔断时长(单位秒)
    rule.setTimeWindow(30);
    // 统计时长(单位为 ms) 默认1000
    rule.setStatIntervalMs(60*1000);
    //将规则添加到集合中
    rules.add(rule);
    DegradeRuleManager.loadRules(rules);
}

上面设置的规则的意思是, 在1分钟内请求出现异常的数量超过2次,服务熔断降级, 30秒后回复正常

2.2.3 系统保护规则 (SystemRule)

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

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

可以通过调用 SystemRuleManager.loadRules() 方法来用硬编码的方式定义流量控制规则

/***
 * 系统自我保护
 */
@PostConstruct
private void initSystemRule() {
    //系统自我保护集合
    List<SystemRule> rules = new ArrayList<>();
    //创建系统自我保护规则
    SystemRule rule = new SystemRule();
    //CPU使用率 值为[0,1],-1 (不生效)
    rule.setHighestCpuUsage(0.9);
    //所有入口资源的 QPS,-1 (不生效)
    rule.setQps(10);
    //入口流量的最大并发数,-1 (不生效)
    rule.setMaxThread(5);
    //所有入口流量的平均响应时间,单位:毫秒,-1 (不生效)
    rule.setAvgRt(1000);
    //load1 触发值,用于触发自适应控制阶段,系统最高负载,建议取值 CPU cores * 2.5
    rule.setHighestSystemLoad(20);
    //将规则加入到集合
    rules.add(rule);
    SystemRuleManager.loadRules(rules);
}

可以测试CPU使用率自我保护,使用jmeter测试如下
在这里插入图片描述

2.2.4 访问控制规则 (AuthorityRule)

参看:链接1、链接2
很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel 的访问控制(黑白名单)的功能。黑白名单根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。

授权规则,即黑白名单规则(AuthorityRule)非常简单,主要有以下配置项:

  • resource:资源名,即规则的作用对象
  • limitApp:对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB
  • strategy:限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式

可以通过AuthorityRuleManager.loadRules来加载规则

@PostConstruct
public void initAuthorityRule() {
    AuthorityRule rule = new AuthorityRule();
    rule.setResource("info");
    rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
    rule.setLimitApp("127.0.0.1,demo.com");
    AuthorityRuleManager.loadRules(Collections.singletonList(rule));
}

/**
 * Sentinel提供了 RequestOriginParser 接口来处理访问来源,Sentinel保护的资源如果被访问,
 * 就会调用 RequestOriginParser解析访问来源
 */
@Component
public class IpLimiter implements RequestOriginParser{

    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        return httpServletRequest.getRemoteAddr();
    }
}

2.2.5 热点规则 (ParamFlowRule)

参看:飞一个
何为热点?热点即经常访问的数据。
很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

1: 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
2: 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
在这里插入图片描述
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
要使用热点参数限流功能,需要引入以下依赖

<!--热点参数限流-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-parameter-flow-control</artifactId>
    <version>1.8.1</version>
</dependency>

然后为对应的资源配置热点参数限流规则,并在 entry 的时候传入相应的参数,即可使热点参数限流生效

热点参数规则(ParamFlowRule)类似于流量控制规则(FlowRule)

属性说明默认值
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集群流控相关配置

可以通过 ParamFlowRuleManager 的 loadRules 方法更新热点参数规则

@SentinelResource(value = "search")
@GetMapping(value = "/search/{city}")
public Driver search(@PathVariable(value = "city")String city){
    System.out.println("查询的司机所在城市:"+city);
    //假设查询到了一个司机信息
    Driver driver = new Driver();
    driver.setName("A");
    driver.setId("1");
    return driver;
}
/***
 * 热点参数初始化
 */
@PostConstruct
private static void initParamFlowRules() {
    ParamFlowRule rule = new ParamFlowRule("search")
            //参数下标为0
            .setParamIdx(0)
            //限流模式为QPS
            .setGrade(RuleConstant.FLOW_GRADE_QPS)
            //统计窗口时间长度(单位为秒)默认1秒
            .setDurationInSec(10)
            //流控效果(支持快速失败和匀速排队模式)
            //CONTROL_BEHAVIOR_DEFAULT:限流行为,直接拒绝
            //CONTROL_BEHAVIOR_WARM_UP:限流行为,匀速排队
            //CONTROL_BEHAVIOR_RATE_LIMITER:限流行为,匀速排队
            .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)
            //最大排队等待时长(仅在匀速排队模式生效  CONTROL_BEHAVIOR_RATE_LIMITER)
            //.setMaxQueueingTimeMs(600)
            //最大阈值为5
            .setCount(5);

    // 为特定参数单独设置规则
    //如下配置:当参数值为恩施的时候,阈值到达2的时候则执行限流
    ParamFlowItem item = new ParamFlowItem()
            //参数类型为String类型
            .setClassType(String.class.getName())
            //设置阈值为2
            .setCount(2)
            //需要统计的值
            .setObject(String.valueOf("恩施"));
    rule.setParamFlowItemList(Collections.singletonList(item)); //返回的是不可变的集合,但是这个长度的集合只有1,可以减少内存空间
    //加载热点数据
    ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
}

非热点参数5次限流, 热点参数2次限流

以上是被调用放的熔断降级, 下面聊聊调用方的

2.3 OpenFeign支持

参看:链接
Sentinel 适配了 Feign 组件。如果想使用,除了外还需要 2 个步骤:

1:引入 spring-cloud-starter-alibaba-sentinel 的依赖
3:配置文件打开 Sentinel 对 Feign 的支持:feign.sentinel.enabled=true

  1. 引入sentinel和OpenFeign依赖,配置如下:
<!--sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.2.7.RELEASE</version>
</dependency>
  1. 配置文件中开启Feign支持sentinel,配置如下:
feign:
  #开启SentinelFeign的支持
  sentinel:
    enabled: true

不打开默认使用hytrix, 而且hytrix默认是打开的

2.3.1 fallback

  1. 为Feign接口创建一个实现类:DriverFeignFallback,在实现类中处理程序异常降级处理方法,代码如下:
@Component
public class DriverFeignFallback implements DriverFeign {

    /**
     * status()降级处理方法
     */
    @Override
    public Driver status(String id, Integer status) {
        Driver driver = new Driver();
        driver.setId(id);
        driver.setStatus(status);
        driver.setName("系统比较繁忙,请您稍后再试!");
        return driver;
    }
}
  1. 在DriverFeign接口上添加fallback属性指定降级处理的类,代码如下:
@FeignClient(value = "hailtaxi-driver",fallback = DriverFeignFallback.class)

2.3.2 fallbackFactory

可以为DriverFeign接口创建一个降级处理的工厂对象,在工厂对象中处理程序异常降级处理方法,用工厂对象处理可以拿到异常信息,代码如下:

  1. 创建DriverFeignFallbackFactory
@Component
public class DriverFeignFallbackFactory implements FallbackFactory<DriverFeign> {
    @Override
    public DriverFeign create(Throwable throwable) {
        return new DriverFeign() {
            /**
             * status()降级处理方法
             */
            @Override
            public Driver status(String id, Integer status) {
                Driver driver = new Driver();
                driver.setId(id);
                driver.setStatus(status);
                driver.setName("系统比较繁忙,请您稍后再试!");
                return driver;
            }
        };
    }
}

2、在DriverFeign接口上添加fallbackFactory属性指定讲解处理的类,代码如下:

@FeignClient(value = "hailtaxi-driver",fallbackFactory = DriverFeignFallbackFactory.class)

3 Sentinel集成Gateway

参看:再飞一个
我们的项目流量入口是SpringCloud Gateway,因此重点看Sentinel集成Gateway

3.1 Sentinel对网关支持

Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流
在这里插入图片描述
Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规则和自定义 API 的实体和管理逻辑
从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:

  1. route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
  2. 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组

这两种维度分别对应如下:

  • GatewayFlowRule:网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。
  • ApiDefinition:用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以定义一个 API 叫 my_api,请求 path 模式为 /foo/** 和 /baz/** 的都归到 my_api 这个 API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。

其中网关限流规则 GatewayFlowRule 的字段解释如下:

  • resource:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。
  • resourceMode:规则是针对 API Gateway 的 route(RESOURCE_MODE_ROUTE_ID)还是用户在 Sentinel 中定义的 API 分组(RESOURCE_MODE_CUSTOM_API_NAME),默认是 route。
  • grade:限流指标维度,同限流规则的 grade 字段。
  • count:限流阈值
  • intervalSec:统计时间窗口,单位是秒,默认是 1 秒。
  • controlBehavior:流量整形的控制效果,同限流规则的 controlBehavior 字段,目前支持快速失败和匀速排队两种模式,默认是快速失败。
  • burst:应对突发请求时额外允许的请求数目。
  • maxQueueingTimeoutMs:匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。
  • paramItem:参数限流配置。若不提供,则代表不针对参数进行限流,该网关规则将会被转换成普通流控规则;否则会转换成热点规则。其中的字段:
  • parseStrategy:从请求中提取参数的策略,目前支持提取来源 IP(PARAM_PARSE_STRATEGY_CLIENT_IP)、Host(PARAM_PARSE_STRATEGY_HOST)、任意 Header(PARAM_PARSE_STRATEGY_HEADER)和任意 URL 参数(PARAM_PARSE_STRATEGY_URL_PARAM)四种模式。
    fieldName:若提取策略选择 Header 模式或 URL 参数模式,则需要指定对应的 header 名称或 URL 参数名称。
    pattern:参数值的匹配模式,只有匹配该模式的请求属性值会纳入统计和流控;若为空则统计该请求属性的所有值。(1.6.2 版本开始支持)
    matchStrategy:参数值的匹配策略,目前支持精确匹配(PARAM_MATCH_STRATEGY_EXACT)、子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)和正则匹配(PARAM_MATCH_STRATEGY_REGEX)。(1.6.2 版本开始支持)
    用户可以通过 GatewayRuleManager.loadRules(rules) 手动加载网关规则,或通过 GatewayRuleManager.register2Property(property) 注册动态规则源动态推送(推荐方式)

3.2 GateWay集成Sentinel

参考:Sentinel · alibaba/spring-cloud-alibaba Wiki · GitHub

  1. 引入如下依赖:
<!--Sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.2.7.RELEASE</version>
</dependency>

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

  1. 创建配置类GatewayConfiguration
    自版本2.2.0起,不再需要手动注入SentinelGatewayBlockExceptionHandler和SentinelGatewayFilter
@Configuration
public class GatewayConfiguration {

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /**
     * 限流的异常处理器
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    /***
     * Sentinel路由处理核心过滤器
     * @return
     */
    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }



    @PostConstruct
    public void doInit() {
        // 自定义 api 分组
        initCustomizedApis();
        // 初始化网关流控规则
        initGatewayRules();
    }

    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition("customer_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/order/**")
                            /**
                             * 匹配策略:
                             * URL_MATCH_STRATEGY_EXACT:url精确匹配
                             * URL_MATCH_STRATEGY_PREFIX:url前缀匹配
                             * URL_MATCH_STRATEGY_REGEX:url正则匹配
                             */
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        definitions.add(api1);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();

        rules.add(new GatewayFlowRule("hailtaxi-driver") // 资源名称,可以是网关中的 routeid或者用户自定义的 API分组名称
                .setCount(2) // 限流阈值
                .setIntervalSec(10) // 统计时间窗口默认1s
                .setGrade(RuleConstant.FLOW_GRADE_QPS) // 限流模式
                /**
                 * 限流行为:
                 * CONTROL_BEHAVIOR_RATE_LIMITER 匀速排队
                 * CONTROL_BEHAVIOR_DEFAULT 快速失败(默认)
                 * CONTROL_BEHAVIOR_WARM_UP:
                 * CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER:
                 */
                .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
                //匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效
                .setMaxQueueingTimeoutMs(1000)
                /**
                 * 热点参数限流配置
                 * 若不设置,该网关规则将会被转换成普通流控规则;否则会转换成热点规则
                 */
                .setParamItem(new GatewayParamFlowItem()
                        /**
                         * 从请求中提取参数的策略:
                         * PARAM_PARSE_STRATEGY_CLIENT_IP
                         * PARAM_PARSE_STRATEGY_HOST
                         * PARAM_PARSE_STRATEGY_HEADER
                         * PARAM_PARSE_STRATEGY_URL_PARAM
                         */
                                    .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
                        /**
                         * 若提取策略选择 Header 模式或 URL 参数模式,
                         * 则需要指定对应的 header 名称或 URL 参数名称。
                         */
                        .setFieldName("token")
                        /**
                         * 参数的匹配策略:
                         * PARAM_MATCH_STRATEGY_EXACT
                         * PARAM_MATCH_STRATEGY_PREFIX
                         * PARAM_MATCH_STRATEGY_REGEX
                         * PARAM_MATCH_STRATEGY_CONTAINS
                         */
                        .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT)
                        //参数值的匹配模式,只有匹配该模式的请求属性值会纳入统计和流控
                        .setPattern("123456") // token=123456 10s内qps达到2次会被限流
                )
        );

        rules.add(new GatewayFlowRule("customer_api")
                /**
                 * 规则是针对 API Gateway 的 route(RESOURCE_MODE_ROUTE_ID)
                 * 还是用户在 Sentinel 中定义的 API 分组(RESOURCE_MODE_CUSTOM_API_NAME),默认是 route。
                 */
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
                .setCount(2)
                .setIntervalSec(1)
                .setGrade(RuleConstant.FLOW_GRADE_QPS)
        );
        GatewayRuleManager.loadRules(rules);
    }
}

4 Sentinel控制台

查看:官方文档
Sentinel 控制台是流量控制、熔断降级规则统一配置和管理的入口,它为用户提供了机器自发现、簇点链路自发现、监控、规则配置等功能。在 Sentinel 控制台上,我们可以配置规则并实时查看流量控制效果

4.1 Sentinel控制台安装

Sentinel 控制台包含如下功能:

  • 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
  • 监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控。
    规则管理和推送:统一管理推送规则。
  • 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制。

Sentinel控制台安装可以基于jar包启动的方式安装,也可以基于docker安装

docker run --name=sentinel-dashboard -d -p 8858:8858 -d --restart=on-failure bladex/sentinel-dashboard:1.8.0

登录后,效果如下:在这里插入图片描述

4.2 接入控制台

  1. 引入依赖包:
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.2.7.RELEASE</version>
</dependency>
spring:
  cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: 192.168.200.200:8858

4.3 可视化管理

4.3.1 实时监控

同一个服务下的所有机器的簇点信息会被汇总,并且秒级地展示在"实时监控"下。

注意: 实时监控仅存储 5 分钟以内的数据,如果需要持久化,需要通过调用实时监控接口来定制。
在这里插入图片描述

4.3.2 流控规则

可以在【流控规则】页面中新增,点击【流控规则】进入页面新增页面,如下图:
在这里插入图片描述

4.3.3 降级规则

降级规则的熔断策略有3种,分别是慢调用比例、异常比例、异常数
在这里插入图片描述

4.3.4 热点数据

热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制
在这里插入图片描述
拓展知识点:
注意:如果流控模式是链路模式,需要引入如下依赖:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-web-servlet</artifactId>
    <version>1.8.0</version>
</dependency>

创建过滤器:

/***
 * CommonFilter过滤器
 * @return
 */
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new CommonFilter());
    registration.addUrlPatterns("/*");  //过滤所有请求
    // 入口资源关闭聚合
    registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
    registration.setName("sentinelFilter");
    registration.setOrder(1);
    return registration;
}

bootstrap.yml配置:web-context-unify: false

  cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: localhost:8858
      web-context-unify: false  #可根据不同的URL 进行链路限流

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/651183.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2、产品经理的工作内容

上一篇文章&#xff1a;1、产品经理的宏观定义_阿杰学编程的博客-CSDN博客 接下来这个章节里&#xff0c;我们有三个目标。 第一个通过案例&#xff0c;大家要了解一下产品经理的一个主要的工作内容。 第二个理解产品经理的一个重要性。 第三个我们要熟悉一下MVP的概念&…

Vue实战笔记(四) 引入Mavon Editor

大家好&#xff0c;我是半虹&#xff0c;这篇文章来讲如何在 Vue 中引入 Mavon Editor \text{Mavon Editor} Mavon Editor 1、背景介绍 在上篇文章中&#xff0c;我们介绍过如何在 Vue 中引入富文本编辑器 Quill Editor \text{Quill Editor} Quill Editor 在这篇文章中&…

433/315接收芯片 XL520,SOP8封装,适用于低功耗要求产品

XL520是一款高集成度、 低功耗的单片ASK/0OK射频接收芯片。高频信号接收功能全部集成于片内以达到用最少的外围器件和最低的成本获得最可靠的接收效果。 XL520接收芯片为SOP8封装&#xff0c;正常工作电压范围2.0~5.5V&#xff0c;正常工作电流3.0~3.2mA&#xff0c;启动时间2…

pdf可以转换为word文档吗?分享这两个方法给大家!

PDF 是一种常见的文件格式&#xff0c;用于可靠地显示和共享文档。然而&#xff0c;当需要编辑或重用 PDF 内容时&#xff0c;将其转换为可编辑的 Word 文档是一个常见的需求。在本文中&#xff0c;我们将介绍两种方法&#xff0c;以帮助您将 PDF 转换为 Word 文档&#xff0c;…

SpringBoot+Bootstrap图书馆管理系统

主要功能 管理员权限登录&#xff1a; ①管理员拥有最高权限&#xff0c;可以分配角色&#xff0c;使不同角色&#xff08;教师、学生等&#xff09;登录显示不同界面的效果 ②首页、系统设置&#xff1a;菜单管理、角色管理、用户管理、日志管理、数据备份、违规统计、占座统…

Unity基础5——物理检测

一、层级 Layer ​ Unity 中设置了共 32 层 Layer&#xff0c;如图&#xff0c;可以点击 Add Layer 添加自定义的 Layer ​ 通过名字得到层级编号 LayerMask.NameToLayer(string layer) ​ 我们需要通过编号左移构建二进制数&#xff0c;这样每一个编号的层级都是对应位为 1 的…

如何使用Jmeter进行http接口测试?

目录 前言&#xff1a; 一、开发接口测试案例的整体方案&#xff1a; 二、接口自动化适用场景&#xff1a; 三、接口测试环境准备 四、创建工程&#xff1a; 总结&#xff1a; 前言&#xff1a; 本文主要针对http接口进行测试&#xff0c;使用Jmeter工具实现。 Jmter工具设…

HTMLCSS Day08 CSS transition过渡

文章目录 CSS过渡-Transitions-过渡三要素-过渡触发-transition-property 规定应用过渡的 CSS 属性的名称。-transition-duration 定义过渡效果花费的时间。默认是 0。-transition-timing-function 规定过渡效果的时间曲线。默认是 "ease"。-transition-delay 规定过…

史上最全文件类型读写库大盘点!什么?还包括音频、视频?

介绍史上最全PYTHON文件类型读写库大盘点&#xff01;包含常用和不常用的大量文件格式&#xff01;文本、音频、视频应有尽有&#xff01;废话不多说&#xff01;走起来&#xff01; 先给大家快捷总结&#xff1a; 文件格式Python库文本文件内置open函数CSV文件csvJSON文件jso…

信号量实现线程同步代码

信号量&实现线程同步代码 信号量线程同步示例代码 信号量 信号量&#xff08;Semaphore&#xff09;是一种用于多线程编程中的同步工具&#xff0c;用于管理对共享资源的访问。它可以控制同时访问某个资源的线程数量&#xff0c;并提供了对共享资源的互斥访问。 信号量通…

一个支持WinForms换肤的开源组件

博主介绍&#xff1a; &#x1f308;一个10年开发经验.Net老程序员&#xff0c;微软MVP、博客专家、CSDN/阿里云 .Net领域优质创作者&#xff0c;专注于.Net领域知识、开源项目分享&#xff01;&#x1f308; &#x1f6d5;文末获取&#xff0c;加入交流群&#x1f6d5; &#…

java数组(Array)

文章目录 一维数组的使用数组的长度数组元素的引用一维数组的遍历一维数组内存分析 数组元素的默认值多维数组的使用静态初始化动态初始化数组的长度和角标二维数组的遍历内存解析 Arrays工具类的使用 一维数组的使用 int[] arr; int arr1[]; double[] arr2; String[] arr3; …

网工内推 | 1-3年经验,思科、华为、华三厂商认证均可

01 地球村股份有限公司 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、提供技术支持服务&#xff0c;提供设备的告警信息的分析处理及与故障问题定位服务&#xff1b; 2、及时响应在服务时限内完成处理设备故障&#xff0c;包括用户有线无线接入故障、系统故障处理…

数据库第四章(数据库安全性)

1.数据库安全性概述 disiz行 不安全因素&#xff1a; 1.非授权的用户对数据的恶意存取和破坏 2.数据库重要信息泄露 3.数据库环境的脆弱性 如何实现安全控制&#xff1f; 1.用户身份鉴别 口令鉴别 生物鉴别 2.存取控制 3.自主存取控制方法 4.授权与收回 grant and revok…

Map容器(Java)

文章目录 1.容器介绍1.1 容器接口结构1.2 简单解析 2. 容器创建(Member functions)3. 访问操作(Element access)3.1 keySet()3.2 entrySet() 4. 修改操作(Modifiers)4.1 put()4.2 remove()4.3 clear() 5. 容量操作(Member functions)5.1 size()5.2 isEmpety() 6. 其他操作(Othe…

用Swagger生成接口,pom中少了一个library参数,排查了几个小时

前言&#xff1a; 我们一般都会使用swagger生成restful接口&#xff0c;这样可以省不少时间&#xff0c;将更多的精力专注于写业务上。但接口也不是经常写&#xff0c;所以&#xff0c;swagger用的也不熟练。尤其是我喜欢拿之前的接口copy一份&#xff0c;然后在此基础上进行修…

1、springcloud环境搭建

目录 1、创建一个父项目 ​编辑 2、创建子项目 2.1创建订单系统-order ​编辑 2.2创建库存系统-stock 3、创建rest服务 3.1添加web依赖 3.2编写controller 3.3订单中需要调用库存中的扣减库存的接口 通过idea开发工具进行搭建 1、创建一个父项目 通过spring initializr…

QT day4 (time/tcp/draw)

如图所示设计一个闹钟 1、头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QColor> #include <QDebug> #include <QMessageBox> #include <QTimer> //定时器类的头文件 #include <QTime> …

搞定剑桥面试数学题番外篇2:使用多线程并发“加强版”

0. 概览 我们在之前三篇博文中已经介绍了如何用多种语言&#xff08;ruby、swift、c、x64 汇编和 ARM64 汇编&#xff09;实现一道“超超超难”的剑桥数学面试题&#xff1a; 有趣的小实验&#xff1a;四种语言搞定“超超超难”剑桥面试数学题 搞定“超超超难”剑桥面试数学…

【每日挠头算法题(7)】对称的二叉树|二叉树的所有路径

欢迎&#xff01; 前言一、对称的二叉树思路&#xff1a;递归法具体代码如下&#xff1a; 二、二叉树的所有路径思路&#xff1a;递归法具体代码如下&#xff1a; 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a;随着人工智能的不…