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

  • 相关阅读:
    淘宝技术架构演进之路
    单点登录
    [c++11] ——条件变量(Condition Variable)
    std::lock_guard unique_lock
    C++中push_back和emplace_back的区别
    C++11 CAS无锁函数compare_exchange_weak的使用
    C++11新特性之 rvalue Reference(右值引用)
    C++ auto和decltype的区别
    ovs contrack
    周总结03
  • 原文地址:https://www.cnblogs.com/coderD/p/14350114.html
Copyright © 2011-2022 走看看