• Sentinel使用教程


    一、Sentinel简介

    1.sentinel介绍

    Sentinel 是由阿里巴巴中间件团队开发的开源项目,是一种面向分布式微服务架构的轻量级高可用流量控制组件。

    2.sentinel应用场景

    Sentinel 主要以流量为切入点,从流量控制、熔断降级、系统负载保护、实时监控和控制台等多个维度来帮助用户提升服务的稳定性。

    3.sentinel与hystrix

    SentinelHystrix
    隔离策略基于并发数线程池隔离/信号量隔离
    熔断降级策略基于响应时间或失败比率基于失败比率
    实时指标实现滑动窗口滑动窗口(基于 RxJava)
    规则配置支持多种数据源支持多种数据源
    扩展性多个扩展点插件的形式
    基于注解的支持即将发布支持
    调用链路信息支持同步调用不支持
    限流基于 QPS / 并发数,支持基于调用关系的限流不支持
    流量整形支持慢启动、匀速器模式不支持
    系统负载保护支持不支持
    实时监控 API各式各样较为简单
    控制台开箱即用,可配置规则、查看秒级监控、机器发现等不完善
    常见框架的适配Servlet、Spring Cloud、Dubbo、gRPC 等Servlet、Spring Cloud Netflix

    4.sentinel组件介绍

    (1)Sentinel 组成
    ①Sentinel 核心库:Sentinel 的核心库不依赖任何框架或库,能够运行于 Java 8 及以上的版本的运行时环境中,同时对 Spring Cloud、Dubbo 等微服务框架提供了很好的支持。
    ②Sentinel 控制台(Dashboard):Sentinel 提供的一个轻量级的开源控制台,它为用户提供了机器自发现、簇点链路自发现、监控、规则配置等功能。
    (2)基本概念
    ①资源:资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如由应用程序提供的服务或者是服务里的方法,甚至可以是一段代码。
    Sentinel 定义资源的方式有下面几种:适配主流框架自动定义资源、通过 SphU 手动定义资源、通过 SphO 手动定义资源、注解方式定义资源。这个稍后会有使用方法教程。

    其中注解方式定义资源@SentinelResource参数介绍如下:

    参数解释
    valueSentinel资源的名称,我们不仅可以通过url进行限流,也可以把此值作为资源名配置,一样可以限流。
    entryType条目类型(入站或出站),默认为出站(EntryType.OUT)
    resourceType资源的分类(类型)
    blockHandler块异常函数的名称,默认为空
    blockHandlerClass指定块处理方法所在的类。默认情况下, blockHandler与原始方法位于同一类中。 但是,如果某些方法共享相同的签名并打算设置相同的块处理程序,则用户可以设置存在块处理程序的类。 请注意,块处理程序方法必须是静态的。
    fallback后备函数的名称,默认为空
    defaultFallback默认后备方法的名称,默认为空
    defaultFallback用作默认的通用后备方法。 它不应接受任何参数,并且返回类型应与原始方法兼容
    fallbackClassfallback方法所在的类(仅单个类)。默认情况下, fallback与原始方法位于同一类中。 但是,如果某些方法共享相同的签名并打算设置相同的后备,则用户可以设置存在后备功能的类。 请注意,共享的后备方法必须是静态的。
    exceptionsToTrace异常类的列表追查,默认 Throwable
    exceptionsToIgnore要忽略的异常类列表,默认情况下为空

    ②规则:围绕资源而设定的规则。Sentinel 支持流量控制、熔断降级、系统保护、来源访问控制和热点参数等多种规则,所有这些规则都可以动态实时调整。

    二、Sentinel使用说明

    1.控制台Dashboard

    (1)下载地址,选择sentinel-dashboard-1.8.6.jar

    下载即可。
    (2)打开命令窗口,进入jar包存放目录,使用java -jar sentinel-dashboard-1.8.2.jar进行服务启动即可。
    (3)登录网址:http://localhost:8080/,用户名密码为sentinel/sentinel
    在这里插入图片描述

    2.Sentinel 流量控制和熔断降级

    (1)首先我们需要构建一个SpringBoot项目,项目结构如下:
    在这里插入图片描述

    (2)然后在pom.xml引入sentinel的依赖。

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

    (3)application.yml配置

    server:
      port: 8800
    spring:
      #允许循环依赖
      main:
        allow-circular-references: true
      application:
        name: sentinel-service
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
        sentinel:
          transport:
            dashboard: 127.0.0.1:8080
            #指定应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer,
            #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
            #port: 8719
    
    #暴露/actuator/sentinel端点
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    (4)SentinelApplication.java,我这里没有修改

    package com.example.sentinel;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class SentinelApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SentinelApplication.class, args);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    (5)SentinelServerController.java,其中test()是对springboot的联通测试,resource1()是SphU定义资源测试,resource2()是流量监控测试,resource3()是熔断测试。

    package com.example.sentinel.controller;
    
    import com.alibaba.csp.sentinel.Entry;
    import com.alibaba.csp.sentinel.SphU;
    import com.alibaba.csp.sentinel.annotation.SentinelResource;
    import com.alibaba.csp.sentinel.slots.block.BlockException;
    import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
    import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.EventObserverRegistry;
    import com.alibaba.csp.sentinel.util.TimeUtil;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    @Controller
    public class SentinelServerController {
    
    
        @RequestMapping(value = "/test")
        @ResponseBody
        public String test() {
            return "Sentinel server";
        }
    
    
    
        @RequestMapping(value = "/resource1")
        @ResponseBody
        public String resource1() {
            return resource1bySphU();
        }
    
        /**
         * 通过 SphU 手动定义资源
         * @return
         */
        public String resource1bySphU() {
            Entry entry = null;
            try {
                entry = SphU.entry("resource1bySphU");
                //您的业务逻辑 - 开始
                return "resource1";
                //您的业务逻辑 - 结束
            } catch (BlockException e1) {
                //流控逻辑处理 - 开始
                return "resource1 limit";
                //流控逻辑处理 - 结束
            } finally {
                if (entry != null) {
                    entry.exit();
                }
            }
        }
    
        //
        @RequestMapping(value = "/resource2")
        //blockHandler 限流后走的方法
        @SentinelResource(value="resource2byAnnotation",
                blockHandler = "resource2Limit",fallback = "resource2Fallback")
        @ResponseBody
        public String resource2()  {
            return "resource2";
        }
    
        public String resource2Limit(BlockException exception){
            return "您点击太快了,稍后重试!";
        }
    
        @RequestMapping(value = "/resource3/{id}")
        //抛出异常时,提供 fallback 处理逻辑
        //处于熔断开启状态时,原来的主逻辑则暂时不可用,会走fallback的逻辑。
        //在经过一段时间(熔断时长)后,熔断器会进入探测恢复状态(HALF-OPEN),此时 Sentinel 会允许一个请求对原来的主业务逻辑进行调用
        //若请求调用成功,则熔断器进入熔断关闭状态(CLOSED ),服务原来的主业务逻辑恢复,否则重新进入熔断开启状态(OPEN)
        @SentinelResource(value="resource3byAnnotation",fallback = "resource3Fallback")
        @ResponseBody
        public String resource3(@PathVariable("id") int id)  {
            monitor();
            System.out.println("主逻辑");
            //这里模拟服务报错
            int defaultId = 5;
            if(id < defaultId){
                throw new RuntimeException ("服务异常");
            }
            return "resource3";
        }
    
        //注意fallback返回值类型必须与原函数一致;方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常;
        //fallback 函数默认需要和原方法在同一个类中,若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析
        public String resource3Fallback (int id){
            return "服务出错,请您先访问这里!";
        }
    
        /**
         * 自定义事件监听器,监听熔断器状态转换
         */
        public void monitor() {
            EventObserverRegistry.getInstance().addStateChangeObserver("logging",
                    (prevState, newState, rule, snapshotValue) -> {
                        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        if (newState == CircuitBreaker.State.OPEN) {
                            // 变换至 OPEN state 时会携带触发时的值
                            System.err.println(String.format("%s -> OPEN at %s, 发送请求次数=%.2f", prevState.name(),
                                    format.format(new Date(TimeUtil.currentTimeMillis())), snapshotValue));
                        } else {
                            System.err.println(String.format("%s -> %s at %s", prevState.name(), newState.name(),
                                    format.format(new Date(TimeUtil.currentTimeMillis()))));
                        }
                    });
        }
    
    
    }
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120

    (6)测试流程
    首先启动springBoot项目,测试resource1方法是否能正常调通。此时在控制台可以看到服务已经监测成功。
    在这里插入图片描述

    在这里插入图片描述
    然后测试resource2方法,先测试resource2方法是否能正常调通。然后在控制台的簇点链路的resource2对应的服务中选择新增流控指标,这里是每秒调用两次就会触发。
    在这里插入图片描述
    新增完后在快速点击该链接,发现流控实现成功
    在这里插入图片描述
    最后测试resource3方法,先测试resource3方法是否能正常调通。我这里设置传参5以上服务调用成功,否则失败。
    在这里插入图片描述
    然后在控制台新增熔断设置,选择策略为异常数,这里代表在一秒内有两个以上请求失败两次以上则会熔断。
    在这里插入图片描述
    可以看到服务调用失败时成功触发了熔断机制,并且正确的调用和错误的调用重复几次,可以看到熔断状态的变化

    在这里插入图片描述

    在这里插入图片描述

    3.常见报错解决

    (1)dashboard控制器启动失败:Web server failed to start. Port 8080 was already in use
    找到该端口的id然后杀掉该进程即可

     netstat -aon|findstr "8080"
     taskkill /pid 11596 /f
    
    • 1
    • 2

    (2)The dependencies of some of the beans in the application context form a cycle:

    org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
    ┌─────┐
    | com.alibaba.cloud.sentinel.SentinelWebAutoConfiguration (field private java.util.Optional com.alibaba.cloud.sentinel.SentinelWebAutoConfiguration.sentinelWebInterceptorOptional)
    └─────┘

    这是循环依赖问题,由于springBoot版本与cloud版本不匹配导致的,可以参考版本说明
    实在没有合适的版本可以采取粗暴的方式直接允许循环依赖。

    spring:
      #允许循环依赖
      main:
        allow-circular-references: true
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    第11章Linux实操篇-Linux磁盘分区、挂载
    算法day36|435,763,56
    unity scene场景调整好后让game窗口的视角与scene相同
    春秋云镜 CVE-2015-9331
    java获取字符串集合中每个字符并且组成一个新的集合实现
    在线代码编辑器CodePen和CodeSandbox
    信息系统项目管理师必背核心考点(四十七)项目分包合同
    ArcGIS:将相邻面进行合并,并不显示中间线条
    Web服务器部署上线的踩坑流程回顾与知新
    为什么用Selenium做自动化测试,你真的知道吗?
  • 原文地址:https://blog.csdn.net/tttalk/article/details/127884281