zoukankan      html  css  js  c++  java
  • 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()+"	 服务不可用");
            }
        }
    复制代码
    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

  • 相关阅读:
    Java 第十一届 蓝桥杯 省模拟赛 洁净数
    Java 第十一届 蓝桥杯 省模拟赛 第十层的二叉树
    Java 第十一届 蓝桥杯 省模拟赛 第十层的二叉树
    Java 第十一届 蓝桥杯 省模拟赛 第十层的二叉树
    Java 第十一届 蓝桥杯 省模拟赛 70044与113148的最大公约数
    Java 第十一届 蓝桥杯 省模拟赛 70044与113148的最大公约数
    20. Valid Parentheses
    290. Word Pattern
    205. Isomorphic Strings
    71. Simplify Path
  • 原文地址:https://www.cnblogs.com/coderD/p/14350114.html
Copyright © 2011-2022 走看看