微服务架构入门之流量控制与熔断降级解决方案----Sentinel初体验

·  阅读 2140
微服务架构入门之流量控制与熔断降级解决方案----Sentinel初体验

写在前面

在上一篇文章当中,我们学会了使用nacos搭建服务注册中心和配置中心,并通过其内部集成的Ribbon+RestTemplate实现了服务调用。

在这篇文章当中,我们将一起学习一个新的组件Sentinel.

Sentinel简介

Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助用户保障微服务的稳定性。

简单来说就是阿里版的豪猪

官网: github.com/alibaba/Sen…

下载地址: github.com/alibaba/Sen…

Sentinel 分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

为什么需要流量控制?

在这里,我谈谈我个人的理解。在一个微服务架构模式下的系统当中,会存在很多个服务,这些服务彼此之间或多或少会存在调用关系。假设服务A1调用了服务B1,服务B又调用了服务C1。在服务C1由于某种原因,出现了阻塞,然后服务B1、A1都在等待C1的响应结果。此时如果在没有感知到这三个节点的状态的情况下,又恰好有许多请求转发到了C1上,使得C1出现了过载现象,进而由点影响到线,再由线影响到面,进而导致大范围的服务不可用,我们把这种现象叫做服务雪崩

因此,我们需要引入一种机制,对服务质量进行监控。如果哪个节点中出现了服务不可用或者是响应缓慢时,我们就减少对其调用的频率,将其工作任务转发给当前节点所在服务集群的其他节点进行处理。过一段时间后再逐渐恢复对其的调用。当然,Sentinel远比我这里所讲的要强大。

为什么选择Sentinel?

Sentinel直接提供界面化的细粒度统一配置,而不需要我们自己搭建监控平台。

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
  • 完备的实时监控:Sentinel 同时提供实时的监控功能。用户可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。用户只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
  • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。用户可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel与其他类似组件比较

SentinelHystrixResilience4j
隔离策略信号量隔离(并发线程数限流)线程池隔离/信号量隔离信号量隔离
熔断降级策略基于响应时间、异常比率、异常数基于异常比率基于异常比率、响应时间
实时统计实现滑动窗口(Leaparray)滑动窗口(基于 RxJava)Ring Bit Buffer
动态规则配置支持多种数据源支持多种数据源有限支持
扩展性多个扩展点插件的形式接口的形式
限流基于QPS,支持基于调用关系的限流有限的支持Rate Limiter
流量整形支持预热模式、匀速器模式、预热排队模式不支持简单的 Rate Limiter 模式
系统自适应保护支持不支持不支持
控制台提供开箱即用的控制台,可配置规则、查看秒级监控、机器发现等简单的监控查看不提供控制台,,可对接其他监控系统

Sentinel的相关概念

资源(Resource)

在Sentinel参与到的系统当中,一切皆可视为资源。Sentinel可以视为保护资源的卫兵。

规则(Rules)

围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

流量控制

这里的流量控制指的是对各个服务节点网络访问请求的控制,根据服务节点的处理能力,动态地调整访问流量阈值。

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

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

服务熔断、服务降级和服务限流

在前文谈到为什么要做流量控制时,跟大家讲到服务雪崩这个概念,熔断降级是防止出现服务雪崩而采取的一种措施。我们知道当一个节点堆积了许多未来的及处理的请求时,就会发生频繁超时、异常比例升高。因此我们需要感知服务调用状态,当出现这中现象时,再有服务请求要访问这个节点,我们就阻止访问,直接返回调用失败的提示,释放资源。然后再隔一段时间后又慢慢地尝试恢复对其访问,测试其有没有恢复,如果恢复,则恢复对其的正常访问。这便是服务熔断。它在服务调用方直接阻止了对服务的调用。

服务降级则是相对于被访问节点而言。当这个节点面对大量的服务请求处理的力不从心是,果断放弃当前节点中的边缘业务,留下资源,用于处理核心业务。当发生对边缘业务的处理请求时,不做真正的业务处理,直接返回一个友好的提示,释放资源,从而保证对核心业务的处理能力。

服务限流则对访问当前节点的请求数直接做出限定,比如规定1秒内,只能有100请求访问当前节点,则第101个服务请求在这一秒内来访问的话,会被直接给拒绝掉。

Sentinel在当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常比例升高的时,对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。

服务熔断作用于服务调用方(服务上游),服务降级作用于服务提供方(服务下游)

Sentinel如何做熔断降级?

Sentinel 对这个问题采取了两种手段:

  1. 通过并发线程数进行限制

    和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。

  2. 通过响应时间对资源进行降级

    除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。

系统负载保护

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

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

如何使用

首先,我们可以根据上文提供的下载地址,将Sentinel下载下来,可以直接下载jar包。

CentOS下:

mkdir /opt/sentinel
cd /opt/sentinel
wget https://github.com/alibaba/Sentinel/releases/download/v1.8.0/sentinel-dashboard-1.8.0.jar
# 后台运行(防火墙开放端口省略)
nohup java -jar sentinel-dashboard-1.8.0.jar &
复制代码

然后打开http://ip:8080/即可访问

输入用户名和密码,默认都是sentinel,即可访问控制台

与Spring Cloud整合

我们在原有的项目模块种新建一个sentinel-demo用于演示sentinel的使用

引入依赖

<dependencies>
    
        <!--        nacos服务发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    
        <!--        nacos配置中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
    
		<!--        sentinel服务流控-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

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

    </dependencies>
复制代码

编写启动类

/**sentinel演示模块启动类
 * @author 赖柄沣 bingfengdev@aliyun.com
 * @version 1.0
 * @date 2020/10/26 10:10
 */
@SpringBootApplication(scanBasePackages = "pers.lbf.spring.cloud.alibaba.demo.sentinel.demo")
@EnableDiscoveryClient
public class SentinelDemoApp {

  public static void main(String[] args) {
    SpringApplication.run(SentinelDemoApp.class,args);
  }
}
复制代码

编写配置

server:
  port: 9004
spring:
  application:
    name: sentinel-demo
  cloud:
    nacos:
      server-addr: 172.17.172.49:8848
    sentinel:
      transport:
        dashboard: 127.0.0.1:8080
复制代码

编写测试接口

/**测试接口
 * @author 赖柄沣 bingfengdev@aliyun.com
 * @version 1.0
 * @date 2020/10/26 10:37
 */
@RestController
@RequestMapping("/test")
public class DemoController {

    @GetMapping("/info")
    public String info() {
        return "Wxb: Java开发实践";
    }
}
复制代码

####启动并访问测试接口

  1. 启动nacos
cd /opt/nacos/bin
sh startup.sh
复制代码

  1. 启动sentinel
cd /opt/sentinel
nohup java -jar sentinel-dashboard-1.8.0.jar
复制代码

  1. 启动服务并访问接口

  1. 查看Sentinel控制台

然后我们可以在Sentinel控制台当中对熔断、降级、限流等进行细粒度的配置。一个很好的原则是:约定>配置>编码。

规则配置

流量控制(flow control)

**原理:**监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。同一个资源可以创建多条限流规则。

QPS: Queries-per-second,每秒查询率。

一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:

  • resource:资源名,即限流规则的作用对象,默认为资料的访问路径去掉根路径例如资源http://localhost:9004/test/info的资源名为/test/info
  • count: 限流阈值
  • grade: 限流阈值类型(QPS 或并发线程数)
  • limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
  • strategy: 调用关系限流策略
  • controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)
  • clusterMode:是否集群

流量控制主要有两种统计类型,一种是统计并发线程数,另外一种则是统计 QPS。当设定好并发线程数阈值或者QPS阈值后,如果在限定时间内,下一次访问将超过阈值,则该次访问失败。

并发线程数控制

并发数控制用于保护业务线程池不被慢调用耗尽。Sentinel简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。

QPS流量控制

当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:直接拒绝Warm Up匀速排队

直接拒绝: 这种方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。

Warm Up:即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。

匀速排队:严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。当请求到来的时候,如果当前请求距离上个通过的请求通过的时间间隔不小于预设值,则让当前请求通过;否则,计算当前请求的预期通过时间,如果该请求的预期通过时间小于规则预设的 timeout 时间,则该请求会等待直到预设时间到来通过(排队等待处理);若预期的通过时间超出最大排队时长,则直接拒接这个请求。

这种方式适合用于请求以突刺状来到,这个时候我们不希望一下子把所有的请求都通过,这样可能会把系统压垮;同时我们也期待系统以稳定的速度,逐步处理这些请求,以起到削峰填谷的效果,而不是拒绝所有请求。

针对来源(调用方)的流控配置:

  • default:表示不区分调用者,来自任何调用者的请求都将进行限流统计。如果这个资源名的调用总和超过了这条规则定义的阈值,则触发限流。
  • {some_origin_name}:表示针对特定的调用者,只有来自这个调用者的请求才会进行流量控制。例如 NodeA 配置了一条针对调用者caller1的规则,那么当且仅当来自 caller1NodeA 的请求才会触发流量控制。
  • other:表示针对除 {some_origin_name} 以外的其余调用方的流量进行流量控制。例如,资源NodeA配置了一条针对调用者 caller1 的限流规则,同时又配置了一条调用者为 other 的规则,那么任意来自非 caller1NodeA 的调用,都不能超过 other 这条规则定义的阈值。

注意:同一个资源名可以配置多条规则,规则的生效顺序为:{some_origin_name} > other > default

流控模式配置:

  • 直接:针对资源本身,接口达到限流条件时,直接限流。

  • 链路:Sentinel 允许只根据某个入口的统计信息对资源限流。比如存在不同的调用链路A、B,我们设置从节点a(a是调用链路A当中的一个节点)过来的调用,将被记录到统计信息当中,那么调用链路B上过来的调用将不被记录到统计信息当中。

  • 关联:当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_dbwrite_db 这两个资源分别代表数据库读写,我们可以给 read_db 设置限流规则来达到写优先的目的。(死贫道不死道友模式)

测试:

  1. 我们可以在Sentinel控制台中进行相关设置:

  1. 然后我们在JMeter中进行测试:设定每秒请求数为10并运行测试

运行测试,查看结果树,可以看到,第4次请求超过了设定的阈值,请求失败

控制台可以查看到实时的结果

熔断降级配置

Sentinel 提供以下几种熔断策略:

  • 慢调用比例 (SLOW_REQUEST_RATIO):

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

  • 异常比例 (ERROR_RATIO):

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

  • 异常数 (ERROR_COUNT):

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

熔断降级规则(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 引入)

我们可以在界面当中进行相应的配置

**注意:**Sentinel 的断路器是 没有半开状态 的.

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

热点参数配置

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

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

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

参数例外项,可以针对指定的参数值单独设置限流阈值,不受前面 配置阈值的限制。仅支持基本类型和字符串类型

热点参数规则(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集群流控相关配置

权限规则配置

我们可以对资源访问进行权限控制。

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

个人建议优先使用白名单机制。

集群流控配置

为什么要使用集群流控呢?

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

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

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

  • Token Client:集群流控客户端,用于向所属 Token Server 通信请求 token。集群限流服务端会返回给客户端结果,决定是否限流。
  • Token Server:即集群流控服务端,处理来自 Token Client 的请求,根据配置的集群规则判断是否应该发放 token(是否允许通过)。
启动方式
  • 独立模式(Alone),即作为独立的 token server 进程启动,独立部署,隔离性好,但是需要额外的部署操作。独立模式适合作为 Global Rate Limiter 给集群提供流控服务。
  • 嵌入模式(Embedded),即作为内置的 token server 与服务在同一进程中启动。在此模式下,集群中各个实例都是对等的,token server 和 client 可以随时进行转变,因此无需单独部署,灵活性比较好。但是隔离性不佳,需要限制 token server 的总 QPS,防止影响应用本身。嵌入模式适合某个应用集群内部的流控。

对于开源版而言,我们可以直接在控制台当中进行相应的配置。

系统规则配置

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

系统规则支持以下的模式:

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

写在最后

在这篇文章当中,我们主要是简单地了解了Sentinel在服务治理方面的相关能力,并通过一个入门案例,了解Sentinel的相关配置。 大家可以发现,配置信息只是保存在了内存当中,服务重启之后,配置信息将会丢失。这显然无法用于我们的实际项目当中去。在下一篇文章当中,我将介绍如何通过集成Nacos的方式,实现Sentinel配置信息的持久化存储。 如果您觉得这篇文章能给您带来帮助,那么可以点赞鼓励一下。如有错误之处,还请不吝赐教。在此,谢过各位乡亲父老!

本文代码链接: github.com/code81192/a…

分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改