我正在参与「启航计划」

SpringCloud实践系列(一):Nacos注册中心

Nacos: 注册中心,处理服务注册与发现

SpringCloud实践系列(二):Ribbon负载均衡

Ribbon: 客户端的负载均衡器,处理服务集群的负载均衡

SpringCloud实践系列(三):OpenFeign服务调用

OpenFeign:声明式的HTTP客户端,服务长途调用

SpringCloud实践系列(四):Nacos装备中心

Nacos:装备中心,中心化办理装备文件

SpringCloud实践系列(五):Sentinel流控

Sentinel:微服务流量卫兵,以流量为进口,维护微服务,避免呈现服务雪崩

SpringCloud实践系列(六):Gateway网关(待更新)

Gateway: 微服务网关,服务集群的进口,路由转发以及负载均衡(结合Sentinel)

SpringCloud实践系列(七):Sleuth链路追寻(待更新)

Sleuth: 链路追寻,链路快速整理、毛病定位等

SpringCloud实践系列(八):Seata分布式业务(待更新)

Seata: 分布式业务处理计划

一、概述

1.1、高并发带来的问题

在微服务架构中,咱们将业务拆分红一个个的服务,服务与服务之间能够彼此调用,可是因为网络原因或许本身的原因,服务并不能确保服务的100%可用,假如单个服务呈现问题,调用这个服务就会呈现网络推迟,此刻若有大量的网络涌入,会形成任务堆积,最终导致服务瘫痪。

1.2、服务雪崩

1.2.1、服务雪崩现象

【现象】

一个服务不可用导致一些列的服务不可用

SpringCloud实践系列(五):Sentinel流控

1.3、常见容错计划

要避免雪崩的分散,咱们就要做好服务的容错,容错说白了便是维护自己不被猪队友拖垮的一些办法。

常见的容错思路有阻隔、超时、限流、熔断、降级这几种。

1.3.1、阻隔机制

比方服务A内总共有100个线程, 现在服务A可能会调用服务B,服务C,服务D.咱们在服务A进行长途调用的时分,给不同的服务分配固定的线程,不会把一切线程都分配给某个微服务. 比方调用服务B分配30个线程,调用服务C分配30个线程,调用服务D分配40个线程. 这样进行资源的阻隔,确保即使下流某个服务挂了,也不至于把服务A的线程耗费完。比方服务B挂了,这时分最多只会占用服务A的30个线程,服务A还有70个线程能够调用服务C和服务D。

SpringCloud实践系列(五):Sentinel流控

1.3.2、超时机制

在上游服务调用下流服务的时分,设置一个最大呼应时刻,假如超越这个时刻,下流未作出反应,就断开恳求,释放掉线程。

SpringCloud实践系列(五):Sentinel流控

1.3.3、限流机制

限流便是约束体系的输入和输出流量已到达维护体系的意图。为了确保体系的稳固运转,一旦到达的需求约束的阈值,就需求约束流量并采取少数办法以完结约束流量的意图。

SpringCloud实践系列(五):Sentinel流控

1.3.4、熔断机制

在互联网体系中,当下流服务因拜访压力过大而呼应变慢或失利,上游服务为了维护体系全体的可用性,能够暂时堵截对下流服务的调用。这种献身局部,保全全体的办法就叫做熔断。

服务熔断一般有三种状况:

  1. 熔断封闭状况(Closed):服务没有毛病时,熔断器所在的状况,对调用方的调用不做任何约束。
  2. 熔断敞开状况(Open):后续对该服务接口的调用不再经过网络,直接执行本地的fallback办法。
  3. 半熔断状况(Half-Open):测验康复服务调用,允许有限的流量调用该服务,并监控调用成功率。假如成功率到达预期,则说明服务已康复,进入熔断封闭状况;假如成功率依旧很低,则从头进入熔断封闭状况。

SpringCloud实践系列(五):Sentinel流控

1.3.5、降级机制

降级其实便是为服务供给一个兜底计划,一旦服务无法正常调用,就运用兜底计划。

SpringCloud实践系列(五):Sentinel流控

1.4、常见容错组件

Hystrix Resilience4J Sentinel
Hystrix是由Netflflix开源的一个推迟和容错库,用于阻隔拜访长途体系、服务或许第三方库,避免级联失利,从而提高体系的可用性与容错性。 Resilicence4J一款十分轻量、简略,而且文档十分清晰、丰富的熔断工具,这也是Hystrix官方推荐的代替产品。不仅如此,Resilicence4j还原生支撑Spring Boot 1.x/2.x,而且监控也支撑和prometheus等多款主流产品进行整合。 Sentinel 是阿里巴巴开源的一款断路器完结,本身在阿里内部现已被大规模采用,十分安稳。

SpringCloud实践系列(五):Sentinel流控

1.5、Sentinel

1.5.1、什么是Sentinel

Sentinel (分布式体系的流量防卫兵) 是阿里开源的一套用于服务容错的综合性处理计划。它以流量为切入点, 从流量控、熔断降级、体系负载维护等多个维度来维护服务的安稳性。

1.5.2、Sentinel特点

SpringCloud实践系列(五):Sentinel流控

  1. 丰富的运用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的中心场景, 例如秒杀(即突发流量操控在体系容量能够承受的规模)、消息削峰填谷、集群流量操控、实时熔断下流不可用运用等。
  2. 齐备的实时监控:Sentinel 供给了实时的监控功用。经过操控台能够看到接入运用的单台机器秒级数据, 乃至 500 台以下规模的集群的汇总运转情况。
  3. 广泛的开源生态:Sentinel 供给开箱即用的与其它开源结构/库的整合模块, 例如与 SpringCloud、Dubbo、gRPC 的整合。只需求引入相应的依靠并进行简略的装备即可快速地接入Sentinel。
  4. 完善的SPI扩展点:Sentinel供给了简略易用、完善的SPI扩展接口。您能够经过完结扩展接口来快速的定制逻辑。例如定制规矩办理、适配动态数据源等。

1.5.3、Sentinel组成部分

Sentinel分为两部分:

  1. 中心库(Java 客户端)不依靠任何结构/库,能够运转于一切 Java 运转时环境,一起对 Dubbo /Spring Cloud 等结构也有较好的支撑。
  2. 操控台(Dashboard)根据 Spring Boot 开发,打包后能够直接运转,不需求额定的 Tomcat 等运用容器。

1.5.4、Sentinel架构

SpringCloud实践系列(五):Sentinel流控

【API调用】常见api

# 接口概览
http://localhost:8720/api
# 获取资源的metrics信息
http://localhost:8720/cnode?id=接口路径
# 获取流控规矩接口
http://localhost:8720/getRules?type=flow
# 设置规矩接口
http://localhost:8720/setRules

二、装置部署Dashboard

下载jar包

github.com/alibaba/Sen…

发动操控台

# 直接到jar包目录下,运用jar指令发动项目(操控台本身是一个SpringBoot项目) 
java -Dserver.port=8088 -Dcsp.sentinel.dashboard.server=localhost:8088 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar

页面拜访

# http://localhost:8088/#/login
# 默许用户名暗码是 sentinel/sentinel

SpringCloud实践系列(五):Sentinel流控

三、快速开端

  • 添加依靠

    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    
  • 装备文件

    spring:
      application:
        name: cloud-order # 服务名
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 # 指定nacos-server的地址
            username: nacos
            password: nacos
            namespace: dev
        sentinel:
          transport:
            port: 8719 # 默许8719(当时服务,对sentinel供给的,调用端口号,用于监控服务拜访数据)
            dashboard: localhost:8888 # dashboard地址
          eager: true # 发动项目是主动注入到sentinel中
    server:
      port: 9003
    

发动项目,拜访http://localhost:8088即可看到,现已注册进去了

四、Sentinel规矩操作

【流控】:当满意被流控规矩,操控流量 — api层

【熔断降级】:当满意规矩,走备用计划 — api层

【热门】:细化到接口的参数,当携带指定参数时,对其做QPS约束 — api的参数层

【体系规矩】: 对整个服务做流控— 整个服务层

【授权】:装备当时服务,那些服务能调用(白名单)、哪些服务不能调用(黑名单)。

SpringCloud实践系列(五):Sentinel流控

4.1、流控

【流控】:当满意被流控规矩,操控流量 — api层

SpringCloud实践系列(五):Sentinel流控

4.1.1、针对来历

针对某个服务做流控

default代表对一切服务的调用做流控

4.1.2、流控规矩

  • QPS

    每秒恳求数

  • 线程数

    一起恳求的线程数量

4.1.3、流控形式

  • 直接(默许)

    接口到达限流条件时,敞开限流

  • 相关

    当相关的资源到达限流条件时,敞开限流 [适合做运用退让]

  • 链路

    当从某个接口,进入当时资源到达限流条件时,敞开限流

4.1.4、流控效果

  • 快速失利(默许)

    直接失利,抛出反常,不做任何额定的处理,是最简略的效果

  • Warm Up

    它从开端阈值到最大QPS阈值会有一个缓冲阶段,一开端的阈值是最大QPS阈值的1/3,然后渐渐增加,直到最大阈值,适用于将忽然增大的流量转换为缓步增加的场景。

    eg:预热时长填10,QPS填900,那么假如一下进来900,会当即完结300,剩余的在10s内完结

  • 排队等待

    让恳求以均匀的速度经过,单机阈值为每秒经过数量,其余的排队等待; 它还会让设置一个超时时刻,当恳求超越超时刻时刻还未处理,则会被丢掉。

4.2、熔断降级

【熔断降级】:当满意规矩,走备用计划 — api层

4.2.1、慢调用份额

SpringCloud实践系列(五):Sentinel流控

上面装备表明

在【计算时长】内,恳求数超越【最小恳求数】,而且恳求中呼应时刻大于【最大RT】的份额超越【份额阈值】,就会触发熔断,在【熔断时长内】直接走降级办法。

4.2.2、反常份额

SpringCloud实践系列(五):Sentinel流控

上面装备表明

在【计算时长】内,恳求数超越【最小恳求数】,而且反常份额超越【份额阈值】,就会触发熔断,在【熔断时长内】直接走降级办法。

4.2.3、反常数

SpringCloud实践系列(五):Sentinel流控

上面装备表明

在【计算时长】内,恳求数超越【最小恳求数】,而且反常数超越【反常数】,就会触发熔断,在【熔断时长内】直接走降级办法。

4.3、热门

【热门】:细化到接口的参数,当携带指定参数时,对其做QPS约束 — api的参数层

SpringCloud实践系列(五):Sentinel流控

上面装备表明

恳求该资源时,携带了第【参数索引】个参数,且在【计算窗口时长】内,QPS到达【单机阈值】则被流控

资源需求运用注解@SentinelResource自界说才干收效

而且,因为运用了该注解,抛出的反常ParamFlowException,需求自己捕获

4.4、体系规矩

【体系规矩】: 对整个服务做流控— 整个服务层

体系规矩支撑以下的形式:

  • 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 到达阈值即触发体系维护。
SpringCloud实践系列(五):Sentinel流控

4.5、授权

【授权】:装备当时服务,那些服务能调用(白名单)、哪些服务不能调用(黑名单)。

SpringCloud实践系列(五):Sentinel流控

上面装备表明

/auth/login接口,不允许cloud-order和cloud-jifen这两个服务调用

五、@SentinelResource及反常捕获

5.1、@SentinelResource运用

5.1、运用规矩

【作用】

1、用在办法上,用于自界说资源名

2、在设置热门时,有必要运用该注解自界说的资源名,不然不收效

@SentinelResource 用于界说资源,并供给可选的反常处理和 fallback 装备项。

属性 作用
value 资源称号,必需项(不能为空)
entryType entry 类型,可选项(默以为 EntryType.OUT
blockHandler/blockHandlerClass blockHandler 对应处理 BlockException 的函数称号,可选项。blockHandler 函数拜访规模需求是 public,返回类型需求与原办法相匹配,参数类型需求和原办法相匹配而且最后加一个额定的参数,类型为 BlockException。blockHandler 函数默许需求和原办法在同一个类中。若希望运用其他类的函数,则能够指定 blockHandlerClass 为对应的类的 Class 目标,留意对应的函数必需为 static 函数,不然无法解析。
fallback/fallbackClass fallback 函数称号,可选项,用于在抛出反常的时分供给 fallback 处理逻辑。fallback 函数能够针对一切类型的反常(除了 exceptionsToIgnore 里边排除去的反常类型)进行处理。fallback 函数签名和方位要求: 1. 返回值类型有必要与原函数返回值类型共同; 2.办法参数列表需求和原函数共同,或许能够额定多一个 Throwable 类型的参数用于接纳对应的反常。 3.fallback 函数默许需求和原办法在同一个类中。若希望运用其他类的函数,则能够指定 fallbackClass 为对应的类的 Class 目标,留意对应的函数必需为 static 函数,不然无法解析。
defaultFallback 默许的 fallback 函数称号,可选项,通常用于通用的 fallback 逻辑(即能够用于许多服务或办法)。默许 fallback 函数能够针对一切类型的反常(除了 exceptionsToIgnore 里边排除去的反常类型)进行处理。若一起装备了 fallback 和 defaultFallback,则只有 fallback 会收效。defaultFallback 函数签名要求: 1. 返回值类型有必要与原函数返回值类型共同; 2. 办法参数列表需求为空,或许能够额定多一个 Throwable 类型的参数用于接纳对应的反常。 3. defaultFallback 函数默许需求和原办法在同一个类中。若希望运用其他类的函数,则能够指定 fallbackClass 为对应的类的 Class 目标,留意对应的函数必需为 static 函数,不然无法解析。
exceptionsToIgnore 用于指定哪些反常被排除去,不会计入反常计算中,也不会进入 fallback 逻辑中,而是会原样抛出。

5.2、运用示例

运用@SentinelResource对流控、降级做自界说处理

  • 方法一:写在同一个类里

    @RestController
    @Slf4j
    public class AnnoController {
        @RequestMapping("/anno1")
        /**
         * 当拜访资源呈现【流控】,会进入 blockHandler 指定的办法去进行处理
         * 当拜访资源呈现 反常/【熔断降级】,会进入到 fallback 去进行处理
         */
        @SentinelResource(value = "anno1",
                					blockHandler="anno1BlockHandler",
                					fallback = "anno1Fallback")
        public String anno1(String name){
            if("wolfcode".equals(name)){
                throw new RuntimeException();
            }
            return "anno1";
        }
      	/**
         * 限流备用办法
         */
        public String anno1BlockHandler(String name, BlockException ex){
            log.error("{}", ex);
            return "接口被限流了";
        }
      	/**
         * 反常或降级备用办法
         */
        public String anno1Fallback(String name, Throwable throwable) {
            log.error("{}", throwable);
          	// 区分熔断降级和其他反常
          	if (throwable instanceof DegradeException) {
              return "熔断降级";
            }
            return "非sentinel反常";
        }
    }
    
  • 写到别的类中

    @RestController
    @Slf4j
    public class AnnoController {
        @RequestMapping("/anno1")
        /**
         * 当拜访资源呈现【限流】,会进入 BlockExceptionHandler类的anno1BlockHandler办法
         */
        @SentinelResource(value = "anno1",
                					blockHandler="anno1BlockHandler",
                					blockHandlerClass="BlockExceptionHandler",
                          fallback = "anno1Fallback",
                          fallbackClass = "DegradeExceptionHandler"
        )
        public String anno1(String name){
            if("wolfcode".equals(name)){
                throw new RuntimeException();
            }
            return "anno1";
        }
    }
    
    • 限流备用办法

      public class BlockExceptionHandler {
        	/**
           * 静态办法
        	*/
        	public static String anno1BlockHandler(String name, BlockException ex){
              log.error("{}", ex);
              return "接口被限流了";
          }
         public static String anno1Fallback(String name, Throwable throwable) {
              log.error("{}", throwable);
            	// 区分熔断降级和其他反常
            	if (throwable instanceof DegradeException) {
                return "熔断降级";
              }
              return "非sentinel反常";
          }
      }
      
    • 反常/降级备用办法

      public class DegradeExceptionHandler {
        	/**
           * 静态办法
        	*/
         public static String anno1Fallback(String name, Throwable throwable) {
              log.error("{}", throwable);
            	// 区分熔断降级和其他反常
            	if (throwable instanceof DegradeException) {
                return "熔断降级";
              }
              return "非sentinel反常";
          }
      }
      

5.2、大局反常捕获

给每个办法都写一个对应的兜底办法,效率太低了

能够运用大局反常捕获对一切办法做兜底

5.2.1、Sentinel反常

SpringCloud实践系列(五):Sentinel流控

5.2.2、大局反常处理

@Slf4j
@ControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice<Object> {
    /**
     * 流控处理
  	*/
    @ExceptionHandler(FlowException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody // 该注解用于把目标转为json
    public OperationInfo handlerFlowException(HttpServletRequest request, HttpServletResponse response, Exception ex) {
        log.info("{}, 流控, {}", request.getRequestURI(), LogUtil.getStack(ex));
        return OperationInfo.failure("流控");
    }
  	/**
     * 熔断降级处理
  	*/
    @ExceptionHandler(DegradeException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public OperationInfo handlerDegradeException(HttpServletRequest request, HttpServletResponse response, Exception ex) {
        log.info("{}, 熔断降级, {}", request.getRequestURI(), LogUtil.getStack(ex));
        return OperationInfo.failure("熔断降级");
    }
  	// 其他略。。。。。。
}

六、规矩耐久化

SpringCloud实践系列(五):Sentinel流控

6.1、默许形式

【规矩】:假如不做任何修正,Dashboard的推送规矩方法是经过API将规矩推送至客户端,并直接更新到内存中

【长处】:简略、无依靠

【坏处】:运用重启规矩会消失,仅用于测试,不能用于出产环境

SpringCloud实践系列(五):Sentinel流控

6.2、Pull形式

【规矩】

FileRefreshableDataSource守时从指定文件中读取规矩JSON文件【本地文件】,假如发现文件发生变化,就更新规矩缓存

FileRefreshableDataSource接纳操控台规矩推送,并根据装备,修正规矩JSON【本地文件】

【长处】

能耐久化装备的规矩

【缺点】

服务部署在多台服务器上,无法同享本地文件

SpringCloud实践系列(五):Sentinel流控

6.3、Push形式

【规矩】

将规矩耐久化在装备中心中,Sentinel把规矩发送到装备中心,各个服务从装备中心中拿对应的规矩

SpringCloud实践系列(五):Sentinel流控

6.3.1、Sentinel Dashboard代码改造

  • Sentinel Dashboard默许是往Sentinel客户端发送,需求修正为往 装备中心发送

    修正源码之后,从头打成jar包运用

6.3.2、微服务端

在要做Sentinel操控的服务里边装备

  • 添加依靠

    <!-- sentinel -->
    <dependency>
    	<groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <!--sentinel的nacos耐久化-->
    <dependency>
    	<groupId>com.alibaba.csp</groupId>
      <artifactId>spring-datasource-nacos</artifactId>
    </dependency>
    
  • 添加装备

    spring:
      cloud:
        nacos:
          config:
            server-addr: localhost:8848
            namespace: dev
            group: DEFAULT_GROUP
            prefix: cloud-order
            file-extension: yml
            username: nacos
            password: nacos
        sentinel:
          transport:
            port: 8730 # 默许8719(当时服务对sentinel供给拜访的端口号)
            dashboard: localhost:8088 # dashboard地址
          eager: true # 发动项目是主动注入到sentinel中
          web-content-unify: false
          datasource:
            flow:
              nacos:
                server-addr: {nacos.server-addr}
                username: {nacos.username}
            		password: {nacos.password}
            		namespace: {nacos.namespace}
            		groupId: DEFAULT_GROUP
                dataId: ${spring.application.name}-flow-rules
                data‐type: json
                rule‐type: flow
            degrade:
            	nacos:
                server-addr: {nacos.server-addr}
                username: {nacos.username}
            		password: {nacos.password}
            		namespace: {nacos.namespace}
            		groupId: DEFAULT_GROUP
                dataId: ${spring.application.name}-degrade-rules
                data‐type: json
                rule‐type: degrade
            param-flow:
              nacos:
                server-addr: {nacos.server-addr}
                username: {nacos.username}
            		password: {nacos.password}
            		namespace: {nacos.namespace}
            		groupId: DEFAULT_GROUP
                dataId: ${spring.application.name}-param-flow-rules
                data‐type: json
                rule‐type: param-flow
            system:
            	nacos:
                server-addr: {nacos.server-addr}
                username: {nacos.username}
            		password: {nacos.password}
            		namespace: {nacos.namespace}
            		groupId: DEFAULT_GROUP
                dataId: ${spring.application.name}-system-rules
                data‐type: json
                rule‐type: system
            authority:
              nacos:
                server-addr: {nacos.server-addr}
                username: {nacos.username}
            		password: {nacos.password}
            		namespace: {nacos.namespace}
            		groupId: DEFAULT_GROUP
                dataId: ${spring.application.name}-authority-rules
                data‐type: json
                rule‐type: authority
    nacos:
    	server-addr: localhost:8848
    	username: nacos
    	password: nacos
    	namespace: sentinel