Sentinel流量控制与熔断降级

📝 学技术、更要掌握学习的方法,一起学习,让进步发生
👩🏻 作者:一只IT攻城狮 ,关注我,不迷路 。
💐学习建议:1、养成习惯,学习java的任何一个技术,都可以先去官网先看看,更准确、更专业。
💐学习建议:2、然后记住每个技术最关键的特性(通常一句话或者几个字),从主线入手,由浅入深学习。
❤️ 《SpringCloud入门实战系列》解锁SpringCloud主流组件入门应用及关键特性。带你了解SpringCloud主流组件,是如何一战解决微服务诸多难题的。

文章目录

  • 一、Sentinel流量控制(flow control)
    • 1、流量控制参数说明
    • 2、自定义异常信息
    • 3、流控效果-快速失败
    • 4、流控效果-Warm Up
    • 5、流控效果-排队等待
    • 6、阈值类型-并发线程数控制
  • 二、Sentinel熔断降级
    • 1、熔断策略-慢调用比例
    • 2、熔断策略-异常比例
    • 3、熔断策略-异常数
  • 三、Sentinel热点参数限流
  • 四、系统规则
  • 五、授权控制规则
  • 六、 集群规则

一、Sentinel流量控制(flow control)

📢:关于Sentinel基本概述和控制台的集成参看:SpringCloud入门实战(十四)Sentinel微服务流量防卫兵简介
❤️:本篇文章重点探讨一下Sentinel关键特性:流量控制与熔断降级的原理和Sentinel控制台的配置。

1、流量控制参数说明

其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

在这里插入图片描述

说明:

规则说明
资源名:唯一名称,默认请求路径
针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default (不区分来源)。
阈值类型/单机阈值:a: QPS(每秒钟的请求数量),当调用该api的QPS达到阈值的时候,进行限流。
b: 线程数:每调用该API的线程数达到阈值的时候,进行限流,用于保护业务线程池不被慢调用耗尽。
是否集群:
流控模式:a: 直接:API达到限流条件时,直接限流。
b: 关联:当关联的资源达到阈值时,就限流自己。
c: 链路:只记录链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【API级别的针对来源】
流控效果:a:快速失败:直接失败,抛异常【默认的流控处理】
b:Warm Up: 即预热/冷启动方式。根据codeFactor (冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值。
c:排队等待:均匀排队,让请求以均速的速度通过,阈值类型必须设置为QPS,否则无效。

同一个资源可以创建多条限流规则。FlowSlot会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。一条限流规则主要由上面几个因素组成,我们可以组合这些元素来实现不同的限流效果。

基于调用关系的流量控制。调用关系包括调用方、被调用方;一个方法可能会调用其它方法,形成一个调用链路的层次关系。

Sentinel 底层采用高性能的滑动窗口数据结构 LeapArray 来统计实时的秒级指标数据,可以很好地支撑写多于读的高并发场景。滑动窗口算法也是Sentinel的默认算法。

2、自定义异常信息

在前面的例子中,无论是熔断降级还是被限流返回的异常信息都是Blocked by Sentinel (flow limiting),这个是Sentinel默认的异常信息。很显然默认的异常信息并不能满足我们的业务需求,因此我们需要根据前后端规则制定自己的异常返回信息。

这里将会用到一个注解@SentinelResource,这个在上文也是提到过,这个注解中有两个关于限流兜底方法的属性,如下:

  • blockHandler: 对应处理 BlockException 的函数名称。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。
  • blockHandlerClass:兜底方法必须要和业务方法放在同一个类中,这样代码耦合度不是很高吗?@SentinelResource提供一个属性blockHandlerClass,完美的解决了这一个问题,能够将兜底方法单独放在一个类中指定。blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。

3、流控效果-快速失败

代码改造:在@SentinelResource注解中指定blockHandler处理BlockException

public class TestController {

    @GetMapping("/testA")
    @SentinelResource(value = "testA",blockHandlerClass = TestController.class,blockHandler = "testBlock")
    public CommonResult<String> testA() {
        return new CommonResult<>(200, "success", "testA");
    }

     public static CommonResult<String> testBlock(BlockException e){
        CommonResult result=new CommonResult();
        result.setCode(5000);
        result.setMessage("Sentinel流量控制!");
        return result;
    }
    
    @GetMapping("/testB")
    public CommonResult<String> testB() {
        return new CommonResult<>(200, "success", "testB");
    }
}

注意📢:value将该方法定义为sentinel的资源,blockHandlerClass指明流控处理的类,blockHandler是流控时调用的方法。

接下来进行简单测试:我们上面配置了阈值为2,然后连续访问2次接口测试,报错,达到限流效果。

在这里插入图片描述

4、流控效果-Warm Up

Warm Up(RuleConstant Control_behavior_warm_up)方式,即预热/冷启动方式。
公式:阈值除以coldFactor(冷加载因子默认值为3),经过预热时间后才能达到阈值。

在这里插入图片描述
举例:如上配置的意思是,阈值为10+预热时长设置为5秒,QPS会从3慢慢过渡到10。
系统初始化的阈值为10/3约等于3,即阈值刚开始为3,然后过了5秒后阈值才慢慢升高恢复到10。
比如,商城的秒杀等系统服务,过一段时间后增加阈值。

5、流控效果-排队等待

匀速排队(Ruleconstant.CONTROL BEHAVIOR RATE LINITER ) 方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。让请求以均匀的速度通过,阈值类型必须设成QPS,否则无效。

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐步处理请求,而不是在第一秒直接拒绝多余的请求。

在这里插入图片描述

举例:如上配置的意思是,testA每秒1次请求,超过的话就排队等待,等待的超时时间为20000毫秒。

注意📢:匀速排队模式暂时不支持 QPS > 1000 的场景

6、阈值类型-并发线程数控制

并发数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。
Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置。

注意📢:sentinel和nacos一样不配置持久化的话,默认是存储在内存中,重启配置失效。

二、Sentinel熔断降级

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。

Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超市或异常比例升高),对这个资源的调用进行限制,请请求快速失败,避免影响到其它的资源而导致级联错误。

当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)。

在这里插入图片描述

说明:

规则说明
资源名:唯一名称,默认请求路径
熔断策略:a.(默认值)慢调用比例
b.异常比例(秒级)
c. 异常数(分钟级)
RTRT:平均响应时间,秒级。
RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max=xxxx才能生效)
熔断时长:窗口期过后关闭断路器
慢调用比例阈值:慢调用比例阀值,仅慢调用比例模式有效 (1.8.0 引入)
比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
最小请求数:熔断触发的最小请求数,请求数小于该值时即使异常比率超出闻值也不会熔断 (1.7.0 引入)
默认值 5
统计时长:统计计时长(单位为 ms),如 60*1000 代表分钟级 (1.8.0 引入)
默认值 1000ms

Sentinel的断路器时没有半开状态的。半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用。具体可以参考Hystrix。

1、熔断策略-慢调用比例

慢调用比例(SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。

在这里插入图片描述

举例:如上配置的意思是,当前允许的最大响应时间是100ms,大于当前值就是慢调用,单位时间内请求数目大于5,并且慢调用比例大于0.2,在接下来的3s内会自动熔断。

2、熔断策略-异常比例

异常比例(ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

在这里插入图片描述

举例:如上配置的意思是,1s内请求数大于5,并且异常比例大于0.4,接下来熔断时长内请求就会自动熔断。

3、熔断策略-异常数

异常数(ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

在这里插入图片描述

举例:如上配置的意思是,当单位统计时长1s内的异常数目超过值2之后会自动进行熔断。

注意📢:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。

三、Sentinel热点参数限流

注意📢:热点参数限流只针对QPS。

顾名思义:热点就是经常访问的数据,很多时候肯定是希望统计某个访问频次Top K数据并对其进行限流。
比如秒杀系统中的商品ID,对于热点商品那一瞬间的并发量是非常可怕的,因此必须要对其进行限流。
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

在这里插入图片描述

配置参数说明:
在这里插入图片描述

注意📢:热点规则需要使用@SentinelResource(“resourceName”)注解,否则不生效。
参数必须是7种基本数据类型才会生效。

在这里插入图片描述

输入http://localhost:8401/hotKey?p1=“1”&p2=“2” 测试:

在这里插入图片描述
上述配置的具体含义:当hotKey这个资源中的第0个参数QPS超过1秒2次将会被限流。
从上图配置中,我们将参数值p1这个参数值等于100的时候,限流阈值设置成了100,也就是说p1=100这个请求QPS放宽到1秒请求100次以上才会被限流。

四、系统规则

前面热点参数、普通流量限流都是针对的某个接口,这里系统自适应限流针对是整个系统的入口流量,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

在这里插入图片描述
阈值类型有五种,分别如下:

阈值类型说明
Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是CPU cores * 2.5
平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。

注意:系统规则中的入口QPS这个规则不建议配置,一旦配置上了该微服务中的所有接口都将会被这个规则限制,可能导致整个服务不可用。

五、授权控制规则

很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。

在这里插入图片描述

来源访问控制规则(AuthorityRule)非常简单,主要有以下配置项:

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

六、 集群规则

为什么要使用集群流控呢?假设我们希望给某个用户限制调用某个 API 的总 QPS 为 50,但机器数可能很多(比如有 100 台)。这时候我们很自然地就想到,找一个 server 来专门来统计总的调用量,其它的实例都与这台 server 通信来判断是否可以调用。这就是最基础的集群流控的方式。

另外集群流控还可以解决流量不均匀导致总体限流效果不佳的问题。假设集群中有 10 台机器,我们给每台机器设置单机限流阈值为 10 QPS,理想情况下整个集群的限流阈值就为 100 QPS。不过实际情况下流量到每台机器可能会不均匀,会导致总量没有到的情况下某些机器就开始限流。因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。

集群流控中共有两种身份:

  • Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
  • Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。

Sentinel 集群流控支持限流规则和热点规则两种规则,并支持两种形式的阈值计算方式:

  • 集群总体模式:即限制整个集群内的某个资源的总体 qps 不超过此阈值。
  • 单机均摊模式:单机均摊模式下配置的阈值等同于单机能够承受的限额,token server 会根据连接数来计算总的阈值(比如独立模式下有 3 个 client 连接到了 token server,然后配的单机均摊阈值为 10,则计算出的集群总量就为 30)

好了,Sentinel控制台配置就介绍到这里了。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[MyBatis系列③]动态SQL

目录 1、简介 2、if标签 3、foreach标签 4、SQL抽取 ⭐MyBatis系列①&#xff1a;增删改查 ⭐MyBatis系列②&#xff1a;两种Dao开发方式 1、简介 开发中在MyBatis映射文件配置SQL语句&#xff0c;但是前面配置的都是比较简单的&#xff0c;不涉及稍复杂的业务场景。想要应…

函数式编程-Stream流学习第二节-中间操作

1 Stream流概述 java8使用的是函数式编程模式,如同它的名字一样&#xff0c;它可以用来对集合或者数组进行链状流式操作&#xff0c;让我们更方便的对集合或者数组进行操作。 2 案例准备工作 我们首先创建2个类一个作家类&#xff0c;一个图书类 package com.stream.model;…

【BUG】解决安装oracle11g或12C中无法访问临时位置的问题

项目场景&#xff1a; 安装oracle时&#xff0c;到第二步出现oracle11g或12C中无法访问临时位置的问题。 解决方案&#xff1a; 针对客户端安装&#xff0c;在cmd中执行命令&#xff1a;前面加实际路径setup.exe -ignorePrereq -J"-Doracle.install.client.validate.cli…

RHCE——八、DNS域名解析服务器

RHCE 一、概述1、产生原因2、作用3、连接方式4、因特网的域名结构4.1 拓扑4.2 分类4.3 域名服务器类型划分 二、DNS域名解析过程1、分类2、解析图&#xff1a;2.1 图&#xff1a;2.2 过程分析 三、搭建DNS域名解析服务器1、概述2、安装软件3、/bind服务中三个关键文件4、配置文…

原生微信小程序 动态(横向,纵向)公告(广告)栏

先看一下动态效果 Y轴滚动公告的原理是swiper组件在页面中的Y轴滚动&#xff0c;属性vertical&#xff0c;其余属性也设置一下autoplay circular interval"3000" X轴滚动的原理是&#xff0c;利用动画效果&#xff0c;将内容从右往左过渡过去 wxml&#xff1a; &l…

DBi Tech Studio Controls for .NET Crack

DBi Tech Studio Controls for .NET Crack Studio Controls for.NET为企业开发人员提供了一套全面的Windows布局和信息表示软件元素&#xff0c;面向搜索业务分析商业调度和UI表示控制器的程序员。Studio Controls for.NET包含17个免版税的.NET Windows窗体控件&#xff0c;用于…

高精度地图定位在高速公路自动驾驶系统中的应用

近年来随着汽车保有量不断增加&#xff0c;随之而来的是: ( 1) 严重的交通拥堵&#xff0c;通行效率低下&#xff0c;用在通行上的时间不断增加; ( 2) 交通事故频发&#xff0c;交通事故导致的伤亡人数和费用不断增加&#xff0c;而且绝大多数事故是由人为因素导致的; ( 3) 大气…

3.2 数据的表现形式及其运算

3.2.1 常量和变量 在计算机高级语言中&#xff0c;数据有两种表现形式&#xff1a;常量和变量。 1.常量 在程序运行过程中&#xff0c;其值不能被改变的量称为常量。如例3.1程序中的5,9,32和例3.2程序中的1000,0.0036,0.0225,0.0198是常量。数值常量就是数学中的常数。 常用的…

lnmp架构-mysql

1.MySQL数据库编译 make完之后是这样的 mysql 初始化 所有这种默认不在系统环境中的路径里 就这样加 这样就可以直接调用 不用输入路径调用 2.初始化 重置密码 3.mysql主从复制 配置master 配置slave 当master 端中还没有插入数据时 在server2 上配slave 此时master 还没进…

在云原生环境中构建可扩展的大数据平台:方法和策略

文章目录 1. **选择适当的云提供商&#xff1a;**2. **采用容器化和微服务架构&#xff1a;**3. **分层架构设计&#xff1a;**4. **弹性计算资源&#xff1a;**5. **使用分布式计算框架&#xff1a;**6. **数据分区和分片&#xff1a;**7. **使用列式存储&#xff1a;**8. **缓…

002微信小程序云开发API数据库-迁移状态查询/更新索引

文章目录 微信小程序云开发API数据库-迁移状态查询案例代码微信小程序云开发API数据库-更新索引案例代码 微信小程序云开发API数据库-迁移状态查询 在微信小程序中&#xff0c;云开发API数据库是一种方便快捷的数据库解决方案。但是&#xff0c;有时候我们可能需要将云开发数据…

C++笔记之rolling counter(滚动计数器)

C笔记之rolling counter&#xff08;滚动计数器&#xff09; 一个 rolling counter&#xff08;滚动计数器&#xff09;是一个计数器&#xff0c;可以在给定的范围内不断增加&#xff0c;当达到最大值时会从最小值重新开始。 code review! 文章目录 C笔记之rolling counter&…

Python|爬虫和测试|selenium框架模拟登录示例(一)

前言&#xff1a; 上一篇文章Python|爬虫和测试|selenium框架的安装和初步使用&#xff08;一&#xff09;_晚风_END的博客-CSDN博客 大概介绍了一下selenium的安装和初步使用&#xff0c;主要是打开某个网站的主页&#xff0c;基本是最基础的东西&#xff0c;那么&#xff0c;…

redis在linux和windows上的安装配置(解决问题:没有可用软件包 redis)

linux系统 安装 yum install redis安装 在终端输入yum install redis安装。 报错&#xff1a;没有可用软件包 redis。 解决&#xff1a; 运行以下命令更新软件包信息&#xff1a; sudo yum clean all sudo yum update 然后继续尝试yum install redis。 如果还不成功&a…

Redis—Redis介绍(是什么/为什么快/为什么做MySQL缓存等)

一、Redis是什么 Redis 是一种基于内存的数据库&#xff0c;对数据的读写操作都是在内存中完成&#xff0c;因此读写速度非常快&#xff0c;常用于缓存&#xff0c;消息队列、分布式锁等场景。 Redis 提供了多种数据类型来支持不同的业务场景&#xff0c;比如 String(字符串)、…

快速排序笔记

一、quick_sort方法中如果 il,jr 会死循环的分析 1、示例代码 void quick_sort(int a[],int l,int r){if(l>r) return;int il,jr; //此处设置会导致死循环int x num[(lr)>>1];while(i<j){while(a[i] <x); //死循环的地方while(a[--j] >x);if(i<j) swap(a…

Day44|leetcode 518.零钱兑换II、377. 组合总和 Ⅳ

完全背包理论基础 视频链接&#xff1a;带你学透完全背包问题&#xff01; 和 01背包有什么差别&#xff1f;遍历顺序上有什么讲究&#xff1f;_哔哩哔哩_bilibili 完全背包与01背包不同的地方就是&#xff1a;01背包每种物品只能取一次&#xff0c;而完全背包每种物品可以取…

CSS中如何隐藏元素但保留其占位空间(display:none vs visibility:hidden)?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ display: none;⭐ visibility: hidden;⭐ 如何选择⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为…

可解释性的相关介绍

一、可解释性的元定义&#xff08;Meta-definitions of Interpretability&#xff09; The extent to which an individual can comprehend the cause of a model’s outcome. [1]The degree to which a human can consistently predict a model’s outcome. [2] 可解释性&am…

深入理解Reactor模型的原理与应用

1、什么是Reactor模型 Reactor意思是“反应堆”&#xff0c;是一种事件驱动机制。 和普通函数调用的不同之处在于&#xff1a;应用程序不是主动的调用某个 API 完成处理&#xff0c;而是恰恰相反&#xff0c;Reactor逆置了事件处理流程&#xff0c;应用程序需要提供相应的接口并…
最新文章