10--SpringCloud Alibaba:Sentinel周阳老师

2021:10--SpringCloud Alibaba:Sentinel

https://www.cnblogs.com/coderD/p/14350076.html SpringCloud

https://www.cnblogs.com/coderD/p/14350073.html SpringCloud 和 Eureka

https://www.cnblogs.com/coderD/p/14350082.html SpringCloud 和 Zookeeper

https://www.cnblogs.com/coderD/p/14350086.html SpringCloud-Ribbon/OpenFeign

https://www.cnblogs.com/coderD/p/14350091.html SpringCloud:Hystrix 断路器

https://www.cnblogs.com/coderD/p/14350097.html SpringCloud:服务网关 gateway

https://www.cnblogs.com/coderD/p/14350099.html SpringCloud:Config/Bus

https://www.cnblogs.com/coderD/p/14350103.html SpringCloud:Stream/Sleuth

https://www.cnblogs.com/coderD/p/14350110.html SpringCloud Alibaba:Nacos

https://www.cnblogs.com/coderD/p/14350114.html SpringCloud Alibaba:Sentinel

https://www.cnblogs.com/coderD/p/14350119.html SpringCloud Alibaba:Seata

代码:https://gitee.com/xue--dong/spring-cloud

阳哥脑图:https://gitee.com/xue--dong/spring-cloud

SpringCloud Alibaba Sentinel 实现熔断与限流

官网

一个轻量级的面向云原生微服务的流量控制,熔断降级组件。

监控保护微服务。
复制代码

中文文档

Sentinel:分布式系统的流量防卫兵。Hystrix的阿里版。

看一下我们以前学习的Hystrxi所具有的缺陷:

    1.  需要我们自己搭建监控平台微服务。
    
    2.  没有一套web界面可以给我们进行更细粒化话的配置
        
        流量监控,速率控制,服务熔断,服务降级......
        
        细化的监控要求越多,Hystrix的配置越多,各种@HystrixCommand的注解标签...
        
        Sentinel:现在做成一个网站,我们在网站上进行配置即可。
        
Sentinel:
    
    单独一个组件,可以独立出来,不需要搭建相应的监控微服务。
    
    支持界面化的细粒度统一配置。
    
约定 > 配置 > 编码:
    
    这些配置我们都可以写在代码里,但是现在的趋势是:约定 > 配置 > 编码。所以我们本次
    还是大规模的学习使用配置和注解,尽量少些代码。
    

主要特征:
复制代码

img

img

一句话:就是我们之前讲的Hystrix
复制代码

Sentinel 的下载安装

官网

img

怎么用

官网手册

主要应对以下场景:
    
    服务雪崩
    服务降级
    服务熔断
    服务限流
复制代码

img

Sentibel 分为两个部分

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

Sentinel 安装

    1.  下载到本地:sentinel-dashboard-1.7.2.jar
    
    2.  运行命令:
        1.前提: JDK1.8,8080端口不能被占用。
        
        命令:java -jar sentinel-dashboard-1.7.2.jar
复制代码

img

        可能会卡住:刷出这些信息就启动好了。
复制代码

img

        后台启动成功。
        
    2.  访问Sentinel管理界面
    
        http://localhost:8080
        
        账号/密码:sentinel/sentinel
复制代码

img

SpringCloud Alibaba Sentinel 初始化演示工程

1. 启动 Nacos8848 成功:startup.cmd

img

2. 新建一个 Module

1.  cloudalibaba-sentinel-service8401
    
2.  POM

    以后基本上:nacos+sentinel一起配。
复制代码
        <!--SpringCloud alibaba Sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        
        <!--spring cloud alibaba nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
复制代码
    完整POM    
复制代码
 <!--SpringCloud alibaba sentinel-datasource-nacos:后续做持久化用到-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <!--SpringCloud alibaba Sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <!--openFeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--spring cloud alibaba nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--web/actuator这两个一般一起使用,写在一起-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--监控-->
        <dependency>
            <groupId>
                org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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

复制代码
3.   yml

    spring.cloud.sentinel.transport.port 端口配置会在应用对应的机器上启动一个 Http Server,
    该 Server 会与 Sentinel 控制台做交互。
    
    比如 Sentinel 控制台添加了1个限流规则,会把规则数据 push 给这个 Http Server 接收,
    Http Server 再将规则注册到 Sentinel 中。

    spring.cloud.sentinel.transport.port:指定应用与Sentinel控制台交互的端口,
    应用本地会起一个该端口占用的HttpServer
复制代码
    server:
      port: 8401
    
    spring:
      application:
        name: cloudalibaba-sentinel-service
      cloud:
        nacos:
          discovery:
            # Nacos服务注册中心地址
            server-addr: localhost:8848
        sentinel:
          transport:
            # 配置Sentinel dashboard地址
            dashboard: localhost:8080
            # 默认8719端口,假如被占用会自动从8719开始一次+1扫描,直至找到被占用的端口。
            port: 8719
    
    management:
      endpoints:
        web:
          exposure:
            include: "*"
复制代码
4.  主启动类
复制代码
    @SpringBootApplication
    @EnableDiscoveryClient
    public class SentinelMainApp8401 {
        public static void main(String[] args) {
            SpringApplication.run(SentinelMainApp8401.class, args);
        }
    }
复制代码
5.  业务类
    
    流控controller:FlowLimitController
复制代码
    @RestController
    public class FlowLimitController {
        
        @GetMapping("/testA")
        public String testA(){
            return "-------testA";
        }
        
        @GetMapping("/testB")
        public String testB(){
            return "--------testB";
        }
    }
复制代码

3. 启动 Sentinel8080

    要敲一下回车
复制代码

img

4. 启动 8401 微服务后查看 sentinel 控制台

img

    空空如也,啥都没有。
    
    原因:Sentinel采用的是懒加载机制
    
    执行一次访问即可:
    
        http://localhost:8401/testA
        http://localhost:8401/testB
    
    效果:8401  cloudalibaba-sentinel-service 被监控到
复制代码

img

    8401微服务已经成功纳入Sentinal:8080,被实时监控。
复制代码

Sentinel 流控规则

1. 基本介绍

    流控规则分为:流控模式和流控效果。
复制代码

img

    选项含义:全的限流属性
复制代码

img

2. 流控模式

    看一下在流控效果:快速失败下的三个流控模式。
复制代码

img

2.1 QPS 直接失败

    QPS:每秒钟的请求数量,当调用该api的QPS达到阈值的时候,进行限流。
    

    1.  直接(默认)失败
    
        新增一个流控规则:
        
            对/testA的访问超过 1/s 直接页面报错
    
        没有修改高级选项:采用系统默认的快速失败。
复制代码

img

img

    2.  测试一下:
    
        1s以内多次发送请求:http://localhost:8401/testA
        
        被限流:被Sentinel直接限流了。
                Blocked by Sentinel (flow limiting)
复制代码

img

        还能继续请求,只要是不超过1/s
        
    
    
    3.  小结
    
        表示1秒钟内查询一次就是OK,若超过次数1,就直接-快速失败,报默认错误。
复制代码

img

    4.  思考:
    
        如果超过了流控规则,页面会报如下错误:
复制代码

img

        直接调用默认的报错信息,技术方面OK。但是是否应该有我们自己的后续处理呢?
        
            应该有类似Hystrix的服务奖fallback的兜底方法。
复制代码

2.2 线程数直接失败

    线程数:当调用该api的线程数达到阈值的时候,进行限流。
    
    线程数1:一次只能处理一个该api:/testA请求
复制代码

img

    演示效果:
复制代码

img

    被Sentinel限流了:
复制代码

img

2.3. 流控模式:关联

    关联:  当关联的资源达到阈值时,就限流自己。
    
            比如当与A关联的资源B达到阈值后,就限流A自己。
            
    比如支付接口达到阈值后,就限流下单订的接口。
    
    
    1.  设置流控规则:
    
        A关联资源B,当B访问量超过1/s,限流A
复制代码

img

    2.  测试一下效果
    
        postman模拟并发密集访问/testB
复制代码

img

img

    3.  效果
    
        20个线程轮流每0.3s访问一下/testB,在此期间显然超过了给A设定得到 流控模式:关联。
        
        被Sentinel限流。
复制代码

img

        大批量线程高并发访问B,导致A失效了。
复制代码

2.4. 流控模式:链路

    多个请求调用同一个微服务。
    
        1.  达到指定的QPS n/s 就会被限流。
        
        2.  调用该API的线程数,达到指定的阈值时,进行限流。
复制代码

3. 流控效果:

    快速失败在上面的流控模式我们演示过了,它是默认的流控效果。
    
    直接失败,抛出异常:Blocked by Sentinel(flow limiting)
复制代码

img

3.1 流控效果:预热 Warm up

    公式:阈值除以coldFactor(默认是3),经过预热时长后才会达到阈值。
    
    Warm up:预热,冷加载
复制代码

img

    QPS设定的是10,预热时长5,这里采用Warm up的流控效果:

        实际上阈值是,即一次性过来10的并发量会被我限流,最开始只能接受10/3的并发量,
    这是为了保护系统遭受突然性的高并发。
        然后阈值开始慢慢上升,最后达到了设定的10,给系统一个适应的过程。
复制代码

img

    测试:
复制代码

img

        狂点请求:
    
        最开始阈值是3/s,会报错限流。
        5s后能抗的住10/s,不会报错了。
复制代码

img

    应用场景:
        
        秒杀系统在开启的瞬间,会有很多流量上来,很可能会把系统杀死,预热方式就是为了保护系统。
        可以慢慢把流量放进来,慢慢的把阈值增长到设置的阈值。
复制代码

3.2 流控效果:排队等待

    匀速排队,阈值只能是QPS不能是线程数。
    
    这种方式主要用于处理间隔性突发的流量,例如消息队列。
    
    想象一下这样的场景:
        
        在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的
        空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
        
    
    理解:
        当阈值超出时,不要直接拒绝,让其排队。
        
        大量的请求到来时,一点一点匀速处理。
复制代码

img

    测试:
        
        Postman模拟请求:20个线程每1s访问一个。
复制代码

img

        结果:
复制代码

img

Sentinel 降级规则

    Circuit-Breaking:熔断降级
复制代码

img

官网

img

1. 降级策略:

1.  RT:平均响应时间,秒级

    平均响应时间:超出阈值且在时间窗口设定的时间内,且通过的请求>=5,两个条件同时满足后触发降级。
    
    窗口期过后关闭断路器。
    
    RT最大49000,更大的需要通过 -Dcsp.sentinel.statistic.max.rt=XXXX才能生效。
    
    注意:  虽然叫做平均响应时间,但是含义是1秒内发过来的所有请求,处理花费的时间。
            所以应该是:处理1s内所有请求话费的时间。
    
1.1 例如:设置平均响应时间200ms,时间窗口1s

    含义:  每秒请求数超过5个,并且请求的响应时间超过0.2s秒时,则下一个请求1s内不可用。
            当时间窗口结束则关闭降级。
            5是一个固定值。
            
            如果1S内发送过来6个请求,但是我在200ms内处理完了不触发。
            
            时间窗口就是不可用时间。
复制代码

img

2.  异常比例(秒级):

        OPS >= 5且异常比例(秒级统计)超过阈值时,触发降级;
        
        时间窗口内服务降级,时间窗口结束,关闭降级。
        
        
3.  异常数(分钟级)

        异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级。
        
        
4.  进一步说明:

        Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高)
        
        对这个资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致的联机错误。
        
        当资源被降级后,在接下来的降级时间窗口内,对该资源的调用都将自动熔断
        (默认抛出DegradeException)
        
5.  Sentinel的断路器是没有半开状态的:
    
        要么断,要么开。
        
        半开的状态:系统会自动去检测请求可以抗住了。可以抗住就打开,不行就继续半开。
复制代码

2. Sentinel 降级:RT

    1.  测试接口:
    
        为了让平均响应时间增大,在代码中加入sleep休眠。
        这样每一个请求的响应时间就会增大,让其平均响应时间大于前面配置的RT,200ms。
        
        实际上RT策略就是模拟服务器可能线程忙,CPU处理不过来,处理的慢的情况。
        
        平时正常时不会降级,当该服务状态差时,就需要减少请求,让其从忙的状态恢复出来,也就是时间
        窗口内开启熔断降级让服务器恢复到正常状态。
        
        RT:是为了让服务器从突然变忙的状态回去过来而设置的。
复制代码
        @GetMapping("/testD")
        public String testD(){
    
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            return "------testD";
        }
复制代码
    2.  新增降级规则:RT的配置
复制代码

img

    3.  测试:
    
        Jmeter压测:     
            并发数10,每次并发时间间隔1s,一直循环。
            一秒10个,一秒10个....
            
        设置的降级规则:RT的配置
            
            允许的平均响应时间:200ms
            熔断降级后:有1s时间恢复。
复制代码

img

        压测开启前:正常
        
            虽然平均响应时间超过200ms,但是并发量只是1,没有超过5,所以没有触发熔断降级。
复制代码

img

        开启压测:
        
            10个线程1秒内,同时访问/testD。
            
            显然会触发熔断降级:
复制代码

img

    4.  开启压测后:
    
        永远一秒钟打进来10个线程(大于5个)调用/testD,我们希望200ms处理完本次任务,
        
        如果200ms还没处理完,在之后1s时间窗口内,断路器打开,微服务不可用。
复制代码

3. Sentinel 降级:异常比例

    异常比例:
        
        当资源的每秒请求量 >=5,并且每秒异常总数占通过量的比值超过阈值之后,资源进入降级状态。
     
    每秒异常占比超过10%时,且每秒的请求超过5次,打开熔断降级:
复制代码

img

    模拟异常:
复制代码

img

    思考这样的情况:
复制代码

img

        我们只访问一次,就报错。
        
        我们会疑惑:只发送了一次请求,虽然错了,但是没有满足一秒超过5次请求。
        为什么弹出这个页面。
        
        要注意这个页面不是熔断降级,是一个正常的异常抛出。
        根本没有进入熔断,走的时候RuntimeException。
        
        因为我们的服务再接下来的1秒内还能正常请求。
复制代码

4. Sentinel 降级:异常数

    异常数:
        当资源近1分钟的异常数目超过阈值后会进行熔断。
        
        注意时间窗口一定要大于60s,如果小于60s,则熔断结束后仍可能在进入熔断状态。
        
        所以时间窗口必须大于60s。
复制代码

img

    测试:
        
        一次请求:
复制代码

img

        发送5次请求后,
复制代码

img

Sentinel 热点 Key 限流。

1. 基本介绍

    1.  热点参数限流
    
    何为热点?即经常访问的数据。
    很多时候我们希望统计某个热点数据中访问频次做高的数据(Top K),并对其进行访问限制。
    
    例如:
        
        商品ID为参数,统计一段时间内最长购买的商品ID并进行限购。
        用户ID为参数,针对一段时间内频繁访问的用户ID进行限制。
        
    热点参数限制会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源
    调用进行限制。
    热电参数限流可以看作是一种特殊的流量控制,仅对包含热电参数的资源调用生效。
    
    Sentinel利用LRU策略统计最近最常访问的热电参数,及任何人令牌桶算法来进行参数级别的流控。
    热电参数限流支持集群模式。
    
    
    2.  承上启下复习:
        
        兜底方法:分为系统默认和客户自定义两种
        
        系统默认:
        之前的case,限流出问题后,都是用sentinel系统默认的提示:Blocked by Sentinel(flow limiting)
        
        我们能不能自定义?类似Hystrix,某个方法出问题了,就找对应的兜底降级方法?
        
        可以:
        从@HystrixCommand到@SentinelResource
复制代码

2. 案例:

1.  测试方法
    
    @SentinelResource(value = "testHotKeyabc001", blockHandler = "del_testHotKey")
    
    分析:
        其中value = "testHotKeyabc001"是一个标识,标识testHotKey方法
        blockHandler = "del_testHotKey":如果违背了Sentinel中配置的流控规则,就会调用我们
        兜底的方法del_testHotKey()
复制代码
    @GetMapping("/testHotKey") //这个代表rest请求地址
    @SentinelResource(value = "testHotKeyabc001", blockHandler = "del_testHotKey") 
    public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
                             @RequestParam(value = "p1", required = false) String p2){

        return "------testHotKey";
    }
    public String del_testHotKey(String p1, String p2, BlockException e){

        return "这次不用默认的兜底提示Blocked by Sentinel(flow limiting),自定义提示:del_testHotKeyo(╥﹏╥)o...";
    }
复制代码
2.  配置热点参数限流规则。

    绑定testHotKey()方法和它的第一个参数p1。
    为其设定一个热点限流规则,当该资源的访问超过1/s时,放生限流执行自定的兜底方法,
复制代码

img

3.  测试:http://localhost:8401/testHotKey?p1=a&p2=b

    1次/s:正常显示
复制代码

img

    迅速点击两次:触发热点参数限流,执行自定义的兜底方法。
复制代码

img

    测试:http://localhost:8401/testHotKey?p2=b
    
    1次/s:正常显示
    
    n/s:仍然正常显示:
    因为是为第0个参数绑定的热单参数限流规则,所以该请求不是参数p1的相关资源,不会触发热点参数限流。
复制代码

img

    同理:http://localhost:8401/testHotKey
    也不会触发热点参数限流。
    
4.  修改测试方法

    没有兜底方法
复制代码

img

    触发热点参数限流会怎么办:
    
    报一个不友好的页面,所以要配置blockHandler:
    
    @SentinelResource(value = "testHotKeyabc001", blockHandler = "del_testHotKey") 
复制代码

img

3. 参数例外项

img

    上一个案例演示了第一个参数p1,为其绑定了一个热点参数限流规则。当其相关的资源QPS超过
    1/s时,触发热点参数限流规则,马上限流。
    
    
    特例情况:
        我们期望p1参数当它是某个特例时,它的限流值和平时不一样。
        
    特例:假如当p1的值等于5时,它的阈值可以达到200...
    
    
    1.  配置规则
    
        当参数p1的值为5时,阈值是200/s
        当参数p1的值不为5,阈值是1/s
复制代码

img

    2.  测试:狂点http://localhost:8401/testHotKey?p1=5
        
        没有触发限流。
复制代码

img

    3.  参数类型
复制代码

img

    4.  测试方法添加一个异常:
复制代码

img

        测试:直接报错
复制代码

img

        要注意:
            
            Sentinel它只管你有没有触发它的限流规则,也可以说只管这个web交互页面(控制台)里面的东西。
            
            配置类的东西Sentinel可以管,java异常的错误我不管。
            
    5.  小结:
    
        @SentinelResource主管配置出错,运行出错该走异常就走异常。
        
        Hystrix中可以通过fallback配置这种情况。
复制代码

Sentinel 系统规则

    类似全局配置:总控的功能。
复制代码

img

    Sentinel系统自适应限流从整体维度对应用入口流量进行控制。
    
    对整个系统进行的规则配置,触发后整个系统限流,类似全局配置。
    
    配置参数:
复制代码

img

img

Sentinel : @SentinelResource

1. 按资源名称限流 + 后续处理

1.  启动nacos+sentinel
复制代码

img

2.  修改module

    cloudalibaba-sentinel-service8401
    
3.  POM

    添加依赖:
复制代码
        <!--引入我们自定义的公共api jar包-->
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
复制代码
4.  业务类RateLimitController

    新建一个业务类:
复制代码
    @RestController
    @Slf4j
    public class RateLimitController {
        
        @GetMapping("/byResource")
        @SentinelResource(value = "byResourceQWER", blockHandler = "handlerException")
        public CommonResult byResource(){
            
            return new CommonResult(200, "按资源名称限流测试OK", new Payment(2020L, "serial001"));
        }
        public CommonResult handlerException(BlockException e){
            return new CommonResult(404, e.getClass().getCanonicalName()+"\t 服务不可用");
        }
    }
复制代码
5.  按资源名称添加限流规则
复制代码

img

6.  测试:

    自测成功
复制代码

img

    请求超过:1/s:触发限流
复制代码

img

7.  问题1:此时关闭服务8401看看:

    刷新:发现刚才为8401中的byResourceQWER资源添加的流控规则消失了
复制代码

img

    显然这种添加的规则是临时的。
复制代码

2. 按照 URL 地址限流 + 后续处理

    1. 测试controller
复制代码
        @GetMapping("/rateLimit/byUrl")
        @SentinelResource(value = "byUrl")
        public CommonResult byUrl(){
            return new CommonResult(200, "按照URL限流测试OK", new Payment(2020L, "serial002"));
        }
复制代码
    2.  自测成功:
复制代码

img

    3.  按照URL配置限流规则
        
        既可以用URL也可以用Resource来指定限流规则:
复制代码

img

        这个没有自定义的兜底的方法,他会用系统默认的。
复制代码

img

3. 我们面临的问题:

    每个方法/API接口我们都要添加一个对应的限流规则吗?---代码膨胀
    
    如果我们要为接口添加自定义的兜底方法,这个处理方法又要和业务代码耦合在一起,一多就很臃肿
    
    但是用系统自带的又没有体现我们的业务要求。
    
    没有一个全局统一的处理方法。
复制代码

4. 客户自定义限流处理类

    要和业务代码解耦
    
    1.  创建CustomerBlockHandler类用于自定义限流处理逻辑
复制代码
        //创建CustomerBlockHandler类用于自定义限流处理逻辑: 提示一个4444异常
        public class CustomerBlockHandler {
        
            public static CommonResult handlerException(BlockException e){
                return new CommonResult(4444, "全局GLOBAL的客户自定义处理---1");
            }
        
            public static CommonResult handlerException2(BlockException e){
                return new CommonResult(4444, "全局GLOBAL的客户自定义处理----2");
            }
        }
复制代码
    2.  指定自定义限流处理类和其中的方法作为兜底的方法
        
        自定义限流处理类:blockHandlerClass = CustomerBlockHandler.class
        自定义限流处理方法:blockHandler = "handlerException2"
            
    @SentinelResource(value = "CustomerBlockHandler",
        blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
        
    
    3.  测试接口    
复制代码
    /**
     * 测试全局的自定义限流处理方法:指定自定义限流处理类和其中的方法作为兜底的方法
     * @return
     */
    @GetMapping("/rateLimit/CustomerBlockHandler")
    @SentinelResource(value = "CustomerBlockHandler",
            blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
    public CommonResult CustomerBlockHandler(){

        return new CommonResult(200, "全局的客户自定义处理", new Payment(2020L, "serial003"));

    }
复制代码
    4.  自测
复制代码

img

    5.  添加限流规则
复制代码

img

    6.  测试:
        
        触发限流规则进入到我们自定义的全局限流处理方法中
复制代码

img

    7.  结构图
复制代码

img

5. 更多注解属性说明

    @SentinelResource注解:
    
    注意:注解方式埋点不支持private方法。
    
    虽然可以用代码做上面这些配置,但是一般我们不会去用,增加代码耦合。
    了解一下3个相关的核心API:
复制代码

img

Sentinel : 服务熔断

主要内容:

    1.  sentinel整合ribbon+openFeign+fallback
    
    2.  Ribbon系列
    
    3.  Feign系列
    
    4.  熔断框架比较
复制代码

1. Ribbon 系列

1.1 提供者微服务

1.  新建module
    
    cloudalibaba-consumer-nacos-order84
    cloudalibaba-provider-payment9003
    cloudalibaba-provider-payment9004
    
2.  9904/9903 POM
复制代码
    <dependencies>

        <!--spring cloud alibaba nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--web/actuator这两个一般一起使用,写在一起-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--监控-->
        <dependency>
            <groupId>
                org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
复制代码
3.  9003/9004 yml
    
    除了端口其他一样。
复制代码
    server:
      port: 9003
    
    spring:
      application:
        name: nacos-payment-provider
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 # 配置nacos地址
    
    management:
      endpoints:
        web:
          exposure:
            include: "*"
复制代码
4.  主启动类
复制代码
    @SpringBootApplication
    @EnableDiscoveryClient
    public class PaymentMain9003 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain9004.class, args);
        }
    }
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class PaymentMain9004 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain9004.class, args);
        }
    }
复制代码
5.  业务类

    我们知道nacos天生就支持负载均衡,因为它自带着ribbon。
    
    Ribbo:支持负载均衡,自带RestTemplate
复制代码
@RestController
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    public static HashMap<Long, Payment> hashMap = new HashMap<>();

    //模拟一个表
    static{
        hashMap.put(1L, new Payment(1L, "qwertyuiop"));
        hashMap.put(2L, new Payment(2L, "asdfghjkl;"));
        hashMap.put(3L, new Payment(3L, "zxcvbnm,."));
    }

    //模拟查表
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
        Payment payment = hashMap.get(id);
        CommonResult<Payment> result = new CommonResult<>(200,
                "from mysql, serverPORT: "+serverPort, payment);

        return result;
    }
}

复制代码

1.2 消费者微服务

1.  cloudalibaba-consumer-nacos-order84

2.  POM
复制代码
    <dependencies>
        <!--spring cloud alibaba nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        
        <!--引入我们自定义的公共api jar包-->
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <!--SpringCloud alibaba sentinel-datasource-nacos:后续做持久化用到-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <!--SpringCloud alibaba Sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <!--openFeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--web/actuator这两个一般一起使用,写在一起-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--监控-->
        <dependency>
            <groupId>
                org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
复制代码
3. yml

# 消费者将要去访问的微服务名册:方便controller的@value获取
server-url:
  nacos-user-service: http://nacos-payment-provider
复制代码
    server:
      port: 84
    
    spring:
      application:
        name: nacos-order-consumer
    
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
        sentinel:
          transport:
            dashboard: localhost:8080 # 配置Sentinel dashboard地址
            port: 8719 #sentinel后台端口
    
    
    # 消费者将要去访问的微服务名册:方便controller的@value获取
    server-url:
      nacos-user-service: http://nacos-payment-provider
复制代码
4.  主启动类
复制代码
    @EnableDiscoveryClient
    @SpringBootApplication
    public class OrderNacosMain84 {
    
        public static void main(String[] args) {
            SpringApplication.run(OrderNacosMain84.class,args);
        }
    }
复制代码
5.  业务类

    因为用的Ribbon,所以使用其提供的RestTemplate
复制代码
package com.atguigu.springcloud.alibaba.config;

@Configuration
public class ApplicationContextConfig {
    
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

复制代码
@RestController
@Slf4j
public class CircuitBreakerController {

    @Value("${server-url.nacos-user-service}")
    private String SERVER_URL;
    //public static final String SERVER_URL = "http://nacos-payment-provider";

    @Resource
    private RestTemplate restTemplate;

    @RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback")
    public CommonResult<Payment> fallback(@PathVariable Long id){
        CommonResult<Payment> result = restTemplate.getForObject(SERVER_URL + "/paymentSQL/"+id, CommonResult.class, id);

        if(id==4){
            throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
        }else if(result.getData() == null) {
            throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }

}
复制代码
6.  目的
    
    加深@SentinelResource(value = "fallback")注解理解:
        1.  fallback管运行异常
        2.  blockHandler管配置违规
        
7.  测试地址:
    
    http://localhost:84/consumer/fallback/1
    
    满足负载均衡:轮询调用9003/9004
复制代码

img

    此时没有配置限流规则也没有自定义的处理方法:所以直接返回一个error Page不友好
    
    http://localhost:84/consumer/fallback/4
复制代码

img

    http://localhost:84/consumer/fallback/5
复制代码

img

8.  配置限流规则:只配置fallback

    类似于Hystrix的服务降级fallback
    
    出现异常,找falback指定的方法。
复制代码

img

    兜底处理异常方法:
复制代码
    //handlerFallback:兜底处理异常方法
    public CommonResult handlerFallback(Long id, Throwable e){
        Payment payment = new Payment(id, "null");

        return new CommonResult<>(444, "兜底异常handlerFallback,exception内容 "+e.getMessage(), payment);
    }

复制代码
    业务类:
复制代码
    @RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback", fallback = "handlerFallback")
    public CommonResult<Payment> fallback(@PathVariable Long id){
        CommonResult<Payment> result = restTemplate.getForObject(SERVER_URL + "/paymentSQL/"+id, CommonResult.class, id);

        if(id==4){
            throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
        }else if(result.getData() == null) {
            throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }
    //handlerFallback:兜底处理异常方法
    public CommonResult handlerFallback(Long id, Throwable e){
        
        Payment payment = new Payment(id, "null");
        //可以把异常带过来
        return new CommonResult<>(444, "兜底异常handlerFallback,exception内容 "+e.getMessage(), payment);
    }
复制代码
    测试:出现异常fallback来管
复制代码

img

9.  配置限流规则:只配置blockHandler

    blockHandler = "blockHandler"
复制代码
    @RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback", blockHandler = "blockHandler")  //fallback = "handlerFallback"
    public CommonResult<Payment> fallback(@PathVariable Long id){
        CommonResult<Payment> result = restTemplate.getForObject(SERVER_URL + "/paymentSQL/"+id, CommonResult.class, id);

        if(id==4){
            throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
        }else if(result.getData() == null) {
            throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }

    //blockHandler:只负责sentinel控制台的违规配置
    public CommonResult blockHandler(@PathVariable Long id, BlockException e){

        Payment payment = new Payment(id, "null");

        return new CommonResult<>(445, "blockHandler-sentinel限流,无此流水:blockException " + e.getMessage());

    }
复制代码
    配置一个降级规则:
复制代码

img

    当资源近1分钟的异常数目超过阈值后会进行熔断,时间窗口60s。
    
    测试:http://localhost:84/consumer/fallback/4
    
    一分钟内报了2次错,触发服务降级熔断。
复制代码

img

    所以:blockHandler只负责sentinel控制台的违规配置


10.  配置限流规则:配置fallback和blockHandler
复制代码

img

    配置了一个异常兜底方法:handlerFallback
    
    配置一个流控
复制代码

img

    限流其效果:
复制代码

img

    没有配置限流时,我们迅速发出异常会被异常兜底方法处理,
    那么配置限流时,我们再迅速发出多次异常请求,会出现什么效果呢?
    
    触发限流。
        
    也即:  同时配置fallback:处理异常和blockHandler:自定义限流处理方法
        
            在不违规限流规则时,走的是fallback指定的异常处理方法,违反限流规则时,走
            blockHandler指定的自定义限流处理方法
复制代码

img

11. 异常忽略属性:exceptionToIgnore

    我们想要某些异常正常的显示出来,好排错。不让我们自定义的异常处理方法处理。
复制代码

img

img

2. Sentinel 熔断:openFeign 系列

    1.  修改84消费者模块:Feign组件一般是消费侧
    
    2.  POM
复制代码
        <!--openFeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
复制代码
    3.  yml
复制代码
        server:
          port: 84
        
        spring:
          application:
            name: nacos-order-consumer
        
          cloud:
            nacos:
              discovery:
                server-addr: localhost:8848
            sentinel:
              transport:
                dashboard: localhost:8080 # 配置Sentinel dashboard地址
                port: 8719 #sentinel后台端口
        
        
        # 消费者将要去访问的微服务名册:方便controller的@value获取
        server-url:
          nacos-user-service: http://nacos-payment-provider
          
        # 激活Sentinel对Feign的支持
        feign:
          sentinel:
            enabled: true
复制代码
    4.  主启动类
复制代码

img

    5.  主业务类
    
        后续我们的controller不找restTemplate,不是restTemplate去调用payment微服务中的接口
        
        调用service:调用paymentServcie,service再去调用payment微服务中的接口。
        
        带着@FeignClient注解的业务接口:
复制代码
1.
package com.atguigu.springcloud.alibaba.service;
public interface PaymentService {
    @Component
    @FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class) 
    //我们要到nacos中找nacos-payment-consumer这个微服务,并且指明了接口出错时的兜底方法
    
    public interface PaymentService {
    
        @GetMapping(value = "/paymentSQL/{id}") //去找nacos-payment-consumer服务中的相应接口
        public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
        
    }
}

2.
package com.atguigu.springcloud.alibaba.service;

public class PaymentFallbackService implements PaymentService {
    @Component
    public class PaymentFallbackService implements PaymentService {
        
        //如果nacos-payment-consumer服务中的相应接口出事了,我来兜底
        @Override
        public CommonResult<Payment> paymentSQL(Long id) {
            
            return new CommonResult<>(4444444,"服务降级返回---PaymentFallbackService", new Payment(id, "errorSerial...."));
        }
    }
}

3.
package com.atguigu.springcloud.alibaba.controller;

public class CircuitBreakerController {
    //===========openFeign
    @Resource
    private PaymentService paymentService;
    
    @GetMapping(value = "/consumer/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
        
        return paymentService.paymentSQL(id);
    }
}
复制代码
    6.  自测:成功
复制代码

img

    7.  关掉9003和9004
    
        不会报404错误,因为我们指明了接口出错时的兜底方法
复制代码

img

Sentinel : 规则持久化

    前面我们微服务新增的限流规则后,微服务关闭后就会丢失,当时配置都限流规则都是临时的。
    
    将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则
    就能看到。只要nacos里面的配置不删除,针对8401上的sentinel上的流控规则就持续存在。
    (也可以持久化到文件,redis,数据库等)
复制代码

步骤

    针对8401:cloudalibaba-sentinel-service8401
    
    1.  POM
        
        添加一个持久化限流规则依赖
复制代码
        <!--SpringCloud alibaba sentinel-datasource-nacos:后续做持久化用到-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
复制代码
    2. yml
复制代码
server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        # Nacos服务注册中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        # 配置Sentinel dashboard地址
        dashboard: localhost:8080
        # 默认8719端口,假如被占用会自动从8719开始一次+1扫描,直至找到被占用的端口。
        port: 8719
        
      # 添加Nacos数据源配置
      datasource: 
        ds1: # 数据源1
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow # 流控规则
    
    
management:
  endpoints:
    web:
      exposure:
        include: "*"
        
feign:
  sentinel:
    enabled: true # 激活Sentinel对Feign的支持
复制代码
    3.  限流规则配置:在nacos写进来一个限流规则配置文件
        
        注意他不是yaml,不同于学nacos时配置yaml文件。
复制代码
[
    {
        "resource": "/rateLimit/byUrl",
        "limitApp": "default",
        "grade": 1,
        "count": 1,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode":  false
    }
]
复制代码

img

img

    也就是:
        为8401微服务中的/rateLimit/byUrl资源配置了一个限流规则。
        
    "grade": 1,   
    "count": 1,
    合起来表示:阈值类型是QPS,且1/s
    

    4.  启动8401,访问一下接口,刷新一下Sentinel
    
        持久化了我们的规则配置文件。
复制代码

img

    5.  关掉8401
        
        停机后流控规则没有了
复制代码

img

    6.  再次重启
        
        发送几次/rateLimit/byUrl请求后,流控规则又出现了。
        
    7.  查看一下数据库
    
        持久化到数据库中了。
复制代码

img

posted @ 2021-01-30 19:21  codeFiler  阅读(299)  评论(0编辑  收藏  举报