• 系列学习 SpringCloud-Alibaba 框架之第 4 篇 —— Sentinel 高可用流量控制组件


    1、概念

            Sentinel 是由阿里巴巴开发的开源项目,面向分布式微服务架构的轻量级高可用流量控制组件。以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度帮助用户保护服务的稳定性。可以说,Sentinel 就是取代 Hystrix 组件的。因为 Hystrix 已经进入了维护状态,不再更新。

    Hystrix 官网:https://github.com/Netflix/Hystrix

    Sentinel 官网:https://github.com/alibaba/Sentinel

    Sentinel 中文使用文档:https://sentinelguard.io/zh-cn/docs/introduction.html

    2、Sentinel 2 大核心

    • 核心库(Java 客户端):就是我们微服务要写的代码。
    • 控制台(Dashboard):Dashboard 主要负责管理推送规则、监控、管理机器信息等(下载 Jar 包运行即可)。参考官网:https://sentinelguard.io/zh-cn/docs/dashboard.html

    3、Sentinel 控制台

    3.1 Sentinel 控制台提供的功能

    • 查看机器列表以及健康情况:Sentinel 控制台能够收集 Sentinel 客户端发送的心跳包,判断机器是否在线。
    • 监控(单机和集群聚合):Sentinel 控制台通过 Sentinel 客户端暴露的监控 API,可以实现秒级的实时监控。
    • 规则管理和推送:通过 Sentinel 控制台,我们还能够针对资源定义和推送规则。
    • 鉴权:从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel。

    3.2 下载控制台的 jar 包

    官网地址:https://github.com/alibaba/Sentinel/releases

     目前最新版本 1.8.6(2022年12月5日)

    3.3 创建启动脚本 .bat

     在我们下载好的 sentinel jar 包的同一级目录,我们创建一个名字叫做:sentinelRun.bat 文件,bat文件是dos下的批处理文件,它包含一条或多条命令。

    然后,使用记事本打开 sentinelRun.bat 文件,输入如下启动命令:

    java -Dserver.port=8898 -Dcsp.sentinel.dashboard.server=localhost:8898 -Dproject.name=sentinel-dashboard -Dsentinel.dashboard.auth.username=admin -Dsentinel.dashboard.auth.password=admin -jar sentinel-dashboard-1.8.6.jar
    pause

    说明:

    • -Dserver.port=8898 用于指定 Sentinel 控制台端口为 8898
    • sentinel-dashboard-1.8.6.jar  要对应我们下载好的 jar 包文件名称。
    • -Dsentinel.dashboard.auth.username=admin 表示设置登录账号名为:admin
    • -Dsentinel.dashboard.auth.password=admin 表示设置登录密码为:admin
    • 默认的账号和密码都是:sentinel,现在我们手动修改为:admin

    3.4 双击 bat 文件启动服务

    如图所示,sentinel 控制台启动成功。

    访问地址:http://localhost:8898/     账号密码都是:admin (端口号、账号密码根据自己配置的来)

     

     页面如图:

     

    注意:sentinel 服务内部通讯端口是 8719 

    4、Sentinel 客户端(具体微服务)

    控制台启动后,客户端(客户端,就是具体的微服务)需要按照以下步骤接入控制台:

    1、添加依赖(pom.xml 依赖)
    2、定义资源(Java 方法)
    3、定义规则

    4.1 添加依赖

    1. <dependency>
    2. <groupId>com.alibaba.cloudgroupId>
    3. <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
    4. <version>2.2.8.RELEASEversion>
    5. dependency>

    说明:如果想查看 sentinel 的版本(注意不是控制台版本,避免混淆),可以查看 Maven 中央仓库:https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-sentinel

    yml 配置文件增加对 sentinel 的配置:

    spring:
      cloud:
        # sentinel 配置
        sentinel:
          transport:
            # 内部通讯端口号
            port: 8719
            dashboard: localhost:8898

    这时候客户端就算搭建好了,但是在 sentinel 的控制台还看不到效果,因为还需要客户端触发一次请求,才能初始化 sentinel 的相关配置。

    4.2 定义资源(Java 方法)

    在客户端某个 controller 类里增加测试代码:

    1. @GetMapping("/demo/sayHello")
    2. @SentinelResource(value = "helloWorld",
    3. blockHandler = "myBlockHandler", fallback = "myFallback")
    4. public String hello(){
    5. return "Hello,Welcome to the Sentinel world!";
    6. }
    7. /**
    8. * 触发了限流,直接拒绝后面的请求。
    9. * @return
    10. */
    11. public String myBlockHandler(BlockException blockException){
    12. blockException.printStackTrace();
    13. return "blockHandler";
    14. }
    15. /**
    16. * 触发了降级,直接返回快速失败的数据(熔断)。
    17. * @return
    18. */
    19. public String myFallback(Throwable throwable){
    20. throwable.printStackTrace();
    21. return "fallback";
    22. }

    说明:

    • 使用注解:@SentinelResource 定义资源,@SentinelResource 注解是 Sentinel 提供的最重要的注解之一,它还包含了多个属性,如下表:
    属性说明必填与否使用要求
    value用于指定资源的名称必填
    entryTypeentry 类型可选项(默认为 EntryType.OUT)
    blockHandler服务限流后会抛出 BlockException 异常,而 blockHandler 则是用来指定一个函数来处理 BlockException  异常的。

    简单点说,该属性用于指定服务限流后的后续处理逻辑。
    可选项
    • blockHandler 函数访问范围需要是 public;
    • 返回类型需要与原方法相匹配;
    • 参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException;
    • blockHandler 函数默认需要和原方法在同一个类中,若希望使用其他类的函数,则可以指定 blockHandler 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
    blockHandlerClass若 blockHandler 函数与原方法不在同一个类中,则需要使用该属性指定 blockHandler 函数所在的类。可选项
    • 不能单独使用,必须与 blockHandler 属性配合使用;
    • 该属性指定的类中的 blockHandler 函数必须为 static 函数,否则无法解析。
    fallback用于在抛出异常(包括 BlockException)时,提供 fallback 处理逻辑。

    fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。
    可选项
    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常;
    • fallback 函数默认需要和原方法在同一个类中,若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
    fallbackClass 若 fallback 函数与原方法不在同一个类中,则需要使用该属性指定 blockHandler 函数所在的类。可选项
    • 不能单独使用,必须与 fallback 或 defaultFallback  属性配合使用;
    • 该属性指定的类中的 fallback 函数必须为 static 函数,否则无法解析。
    defaultFallback默认的 fallback 函数名称,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。

    默认 fallback 函数可以针对所以类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。
    可选项
    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常;
    • defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
    exceptionsToIgnore用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。可选项

    -

    • 如果接口上没有使用 @SentinelResource 注解,默认的资源名称就是接口路径地址。
    • 注意 handler 和 fallback 的方法都需要增加异常类。fallback是针对方法出现异常了,则会进入fallback方法。blockhandler是针对流控设置,超出规则,则会进入blockhandler方法。若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出BlockException时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出。
    • handler 和 fallback 对应的函数,要么在相同的类里面,要么在其它类里是 static 静态函数。

    重启客户端,发送测试请求。http://localhost:9050/demo/sayHello

     然后查看 Sentinel 控制台:

    可以看到我们的客户端(微服务)已经受到 Sentinel 控制了。至此,客户端与 sentinel 打通。

    4.3 定义规则

    规则可以通过配置文件定义,也可以在控制台中定义。在配置文件定义的话,在客户端初始化之后,就会同步到 Sentinel 控制台上来。如果通过控制台设置,在客户端(微服务)重启后失效,也就是说,限流规则并没有持久化在 Sentinel 服务端

    1. 通过控制台设置流控规则

     

     

    设置阈值类型是:QPS (Queries Per Second,意思是“每秒查询率”)。阈值 1 秒内接受1次请求。测试:1秒内多次请求接口,

     模拟程序出现异常的情况:

    1. @GetMapping("/demo/sayHello")
    2. @SentinelResource(value = "helloWorld",
    3. blockHandler = "myBlockHandler", fallback = "myFallback")
    4. public String hello(){
    5. int k = 1/0;
    6. return "Hello,Welcome to the Sentinel world!";
    7. }

     测试:

     第一次请求直接 fallback(因为抛出了异常)。但是后面多次请求后,会出现 handler,因为触发了流控规则。

            2、动态配置规则

    通过控制台新增规则,重启微服务客户端规则就失效了。显然不符合我们实际生产所需。生产上一般通过动态规则源的方式来动态管理规则。因此需要动态配置规则。一方面,可以跟 nacos 整合,另一方面,可以写到配置文件中。

    SentinelProperties 内部提供了 TreeMap 类型的 datasource 属性用于配置数据源信息,支持:

    • 文件配置规则
    • Nacos 配置规则
    • ZooKeeper 配置规则
    • Apoloo 配置规则
    • Redis 配置规则

    讲解一下用到最多的【文件配置规则】,在配置文件中的 sentinel 节点增加 datasource 节点:

    spring:
      cloud:
        # sentinel 配置
        sentinel:
          transport:
            # 内部通讯端口号
            port: 8719
            dashboard: localhost:8898
          datasource:
            ds1:
              file:
                # 配置文件地址和类型
                file: classpath:myRule.json
                data-type: json
                rule-type: flow

    对应的,在项目的根目录增加 myRule.json 文件,文件内容如下:

    [
      {
        "resource": "helloWorld",
        "count": 1,
        "grade": 1,
        "limitApp": "default",
        "strategy": 0,
        "controlBehavior": 0
      }
    ]

    流量控制规则 (FlowRule):

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

    更多动态配置参考官网:dynamic-rule-configuration | Sentinel

    配置完毕,重启客户端的微服务。调用一次接口:

    可以看到流控规则已经包含了我们配置文件里设置的规则了,注意到配置文件里的流控规则不允许修改资源名。生产环境一般都是这样配置使用。 

    5、Sentinel 对 Feign 的支持

    5.1 添加对 feign 的依赖

    1. <dependency>
    2. <groupId>org.springframework.cloudgroupId>
    3. <artifactId>spring-cloud-starter-openfeignartifactId>
    4. <version>2.2.8.RELEASEversion>
    5. dependency>

    代码与 Feign 整合 Hystrix 的一样,如:

    API 接口定义如下:

    1. import org.springframework.cloud.openfeign.FeignClient;
    2. import org.springframework.stereotype.Component;
    3. import org.springframework.web.bind.annotation.GetMapping;
    4. @Component
    5. @FeignClient(name = "xxx-service", fallbackFactory = TestFactory.class, contextId = "TestApi")
    6. public interface TestApi {
    7. @GetMapping("/test")
    8. String test();
    9. }

    注意:我们增加了一个 contextId 等于当前类名,主要是因为旧版本的 springboot 是支持 2 个或者 2 个以上接口类 @FeignClient 有相同的 value 或者 name,但是 SpringBoot 2.2.x 版本以后,就不支持了,会抛出异常:

    could not be registered. A bean with that name has already been defined and overriding is disabled.

    Action:

    Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

    可以通过配置文件里设置:spring.main.allow-bean-definition-overriding=true,也可以通过 contextId 作为区分。推荐后者。

    对应的 TestFactory 类(核心代码):

    1. import feign.hystrix.FallbackFactory;
    2. import lombok.extern.slf4j.Slf4j;
    3. import org.springframework.stereotype.Component;
    4. @Slf4j
    5. @Component
    6. public class TestFactory implements FallbackFactory {
    7. /**
    8. * 如果调用异常,使用熔断机制返回错误信息
    9. * @param cause
    10. * @return
    11. */
    12. @Override
    13. public TestApi create(Throwable cause) {
    14. return new TestApi() {
    15. @Override
    16. public String test() {
    17. log.error("远程调用异常:"+cause);
    18. return "远程调用异常,这是托底数据";
    19. }
    20. };
    21. }
    22. }

    增加一个测试的 controller:

    1. @RestController
    2. public class TestController {
    3. @Autowired
    4. private TestApi testApi;
    5. @GetMapping("/test")
    6. public String test() {
    7. return testApi.test();
    8. }
    9. }

    5.2 配置文件开启对 feign 的支持

    配置文件增加配置:feign.sentinel.enabled=true 即可。

    重启客户端的微服务,发送一次测试请求:http://localhost:9050/test

    这时候是有异常的,因为我们没有配置对应的远程服务,但不影响。我们查看控制台,已经将 feign 接口纳入管理了。

    远程调用异常:java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: xxx-service

     

    只要我们增加对应的远程微服务,提供对应的测试接口,就可以打通整条链路的调用。

  • 相关阅读:
    Spring Boot业务系统如何实现海量数据高效实时搜索
    【python零基础入门学习】python进阶篇之数据库连接-PyMysql-全都是干货-一起来学习吧!!!
    小白免费编程学习网站
    PyQt5快速开发与实战 3.1 Qt Designer快速入门
    java计算机毕业设计敬老福利院管理源码+mysql数据库+系统+lw文档+部署
    计算Qt中的QAudioOutput缓冲区未播放的音频字节数对应时长
    [javaweb]jsp,jstl,el表达式的使用
    【多线程】Java如何实现多线程?如何保证线程安全?如何自定义线程池?
    设计模式——15. 模板方法模式
    XCTF之新手Web题目
  • 原文地址:https://blog.csdn.net/BiandanLoveyou/article/details/128186712