zoukankan      html  css  js  c++  java
  • 【SpringCloud】SpringCloud Alibaba Sentinel实现熔断与限流

    SpringCloud Alibaba Sentinel实现熔断与限流

    限流与降级

    限流 blockHandler
    降级 fallback
    降级需要运行时出现异常才会触发,而限流一旦触发,你连运行的机会都没有,当然就不会降级。
    也就是说,两者如果同时触发,那么一定是限流触发(降级连机会都没有)。

    Sentiel

    官网

    https://github.com/alibaba/Sentinel
    中文:https://github.com/alibaba/Sentinel/wiki/介绍

    是什么

    一句话解释就是我们之前讲过的hystrix

    去哪下

    https://github.com/alibaba/Sentinel/releases

    能干嘛

    怎么玩

    官方文档:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel

    服务中的各种问题

    • 服务雪崩
    • 服务降级
    • 服务熔断
    • 服务限流

    安装Sentiel控制台

    sentinel组件由两部分构成

    • 后台
    • 前台8080

    安装步骤

    下载

    https://github.com/alibaba/Sentinel/releases

    运行命令

    前提

    java8环境OK
    8080端口不能被占用

    命令

    java -jar sentinel-dashboard-1.7.0.jar

    访问sentinel管理界面

    http://localhost:8080

    登录账号密码均为sentinel

    初始化演示功能

    启动Nacos8848成功

    http://localhost:8848/nacos/#/login

    Module

    cloudalibaba-sentinel-service8401

    POM

    <dependencies>
        <!-- SpringCloud ailibaba nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <!-- SpringCloud ailibaba sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <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>

    YML

    server:
      port: 8401
    
    spring:
      application:
        name: cloudalibaba-sentinal-service
      cloud:
        nacos:
          discovery:
            #Nacos服务注册中心地址
            server-addr: localhost:8848
        sentinel:
          transport:
            #配置Sentin dashboard地址
            dashboard: localhost:8080
            # 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口
            port: 8719
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'

    主启动

    @EnableDiscoveryClient
    @SpringBootApplication
    public class MainApp8401 {
    
        public static void main(String[] args) {
            SpringApplication.run(MainApp8401.class,args);
        }
    }

    业务类FlowLimitController

    public class FlowLimitController {
    
        @GetMapping("/testA")
        public String testA() {
            return "----testA";
        }
    
        @GetMapping("/testB")
        public String testB() {
            return "----testB";
        }
    }

    启动Sentinel8080

    java -jar sentinel-dashboard-1.7.0.jar

    启动微服务8401

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

    空空如也,啥也没有

    Sentinel采用懒加载说明

    执行一次访问

    http://localhost:8401/testA
    http://localhost:8401/testB

    效果

    结论

    sentinel8080正在监控微服务8401

    流控规则

    基本介绍

    进一步解释说明


    流控模式

    直接(默认)

    直接->快速失败

    系统默认

    配置及说明

    测试

    • 快速点击访问http://localhost:8401/testA
    • 结果:Blocked by Sentinel(flow limiting)
    • 思考???
      直接调用默认报错信息,技术方面ok,but,是否应该有我们自己的后续处理
      类似有个fallback的兜底方法

    关联

    应用场景:比如支付时达到阈值,可以从源头上比如购买界面,进行限流
    类比:下流洪灾,上流关水

    是什么

    • 当关联的资源达到阈值时,就限流自己
    • 当与A关联的资源B达到阈值后,就限流自己
    • B惹事,A挂了

    配置A

    postman模拟并发密集访问testB

    访问B成功

    postman里新建多线程集合组

    将访问地址添加进新线程组

    RUN

    大批量线程高并发访问B,导致A失效了

    运行后发现testA挂了

    • 点击访问A
    • 结果:Blocked by Sentinel(flow limiting)

    链路

    • 多个请求调用同一个微服务
    • 家庭作业试试

    流控效果

    直接->快速失败(默认的流控处理)

    • 直接失败,抛出异常
      Blocked by Sentinel(flow limiting)
    • 源码
      com.alibaba.csp.sentinel.slots.block.controller.DefaultController

    预热

    说明

    公式:阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值

    官网

    • 默认coldFactor为3,即请求QPS从threshold/3开始,经预热时长逐渐升至设定的QPS阈值
    • 限流 冷启动
      https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8

    源码

    com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController

    WarmUp配置

    多次点击http://localhost:8401/testB

    刚开始不行,后续慢慢OK

    应用场景

    如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,颍热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阀值增长到设置的阀值。

    排队等待

    匀速排队,阈值必须设置为QPS

    官网

    源码

    com.ailibaba.csp.sentinel.slots.block.controller.RateLimiterController

    测试

    降级规则

    官网

    https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7

    基本介绍

    进一步说明

    Sentinel的断路器是没有半开状态的

    Sentinel熔断隆级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高) ,对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。

    当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)。

    半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用,具体参考Hystrix

    复习Hystrix

    降级策略实战

    RT

    是什么


    测试

    代码

    配置

    jmeter压测
    结论


    异常比例

    是什么


    测试

    代码

    配置

    jmeter

    结论

    异常数

    是什么


    异常数是按照分钟统计的

    测试

    代码

    同异常比例

    配置

    jmeter

    热点key限流

    基本介绍

    是什么

    官网

    https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81

    承上启下复习start

    SentinelResource

    兜底方法
    分为系统默认和客户自定义,两种

    之前的case,限流出问题后,都是用sentine|系统默认的提示: Blocked by Sentinel (flow limiting)

    我们能不能自定?类似hystrix,某个方诎问题了,就找对应的兜底降级方法?

    结论
    从HystrixCommand到@SentinelResource

    代码

    com.alibaba.csp.sentinel.slots.block.BlockException

    配置

    配置

    1

    • @SentinelResource(value = "testHotKey")
    • 异常打到了前台用户界面看到,不友好

    2

    • @SentinelResource(value = "testHotKey",blockHandler="dealHandler_testHotKey")
    • 方法testHotKey里面第一个参数只要QPS超过每秒一次,马上降级处理
    • 用了我们自己定义的

    测试

    • × error
      http://localhost:8401/testHotKey?p1=abc
    • × error
      http://localhost:8401/testHotKey?p1=abc&p2=33
    • √ right
      http://localhost:8401/testHotKey?p2=abc

    参数例外项

    上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流

    特殊情况

    普通

    • 超过1秒钟一个后,达到阈值1后马上被限流
    • 我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样
    • 特例:假如当p1的值等于5时,它的阈值可以达到200

    配置


    添加
    按钮不能忘

    测试

    • √ http://localhost:8401/testHotKey?p1=5
    • × http://localhost:8401/testHotKey?p1=3
    • 当p1等于5的时候,阈值变为200
    • 当p1不等于5的时候,阈值就是平常的1

    前提条件

    热点参数的注意点,参数必须是基本类型或者String

    其他

    手贱添加异常看看o(╥﹏╥)o
    后面讲

    @SentinelResource
    处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理;

    RuntimeException
    int age = 10/0, 这个是java运行时报出的运行时异常RunTimeException, @SentinelResource不管

    总结
    @SentinelResource主管配置出错,运行出错该走异常走异常

    系统规则

    是什么

    https://github.com/alibaba/Sentinel/wiki/ 系统自适应限流

    各项配置说明

    配置全局QPS

    不合适,使用危险,一竹竿打死一船人

    @SentinelResource

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

    启动nacos成功

    启动Sentinel成功

    Module

    cloudalibaba-sentinel-service8401

    pom

    <dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
        <groupId>com.eiletxie.springcloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>${project.version}</version>
    </dependency>

    yml

    业务类RateLimitController

    @RestController
    public class RateLimitController {
    
        @GetMapping("/byResource")
        @SentinelResource(value = "byResource",blockHandler = "handleException")
        public CommonResult byResource() {
            return  new CommonResult(200,"按照资源名称限流测试",new Payment(2020L,"serial001"));
        }
    
        public CommonResult handleException(BlockException exception) {
            return  new CommonResult(444,exception.getClass().getCanonicalName() + "	 服务不可用");
        }
    }

    主启动

    配置流控规则

    配置步骤

    图形配置和代码关系

    表示1秒钟内查询次数大于1,就跑到我们自定义的限流处,限流

    测试

    • 1秒钟点击1下,OK
    • 超过上述,疯狂点击,返回了自己定义的限流处理信息,限流发生

    额外问题

    • 此时关闭服务8401看看
    • Sentinel控制台,流控规则消失了?
      临时?持久?

    按照Url地址限流+后续处理

    通过访问URL来限流,会返回Sentinel自带默认的限流处理信息

    业务类RateLimitController

    访问一次

    Sentinel控制台配置

    测试

    • 疯狂点击http://localhost:8401/rateLimit/byUrl
    • 结果

    上面兜底方案面临的问题

    1. 系统默认的, 没有体现我们自己的业务要求。
    2. 依照现有条件,我们自定义的处理方法又和业务代码耦合在-块,不直观。
    3. 每个业务方法都添加一个兜底的,那代码膨胀加剧。
    4. 全局统一的处理方法没有体现。

    客户自定义限流处理逻辑

    创建CustomerBlockHandler类用于自定义限流处理逻辑

    自定义限流处理类

    CustomerBlockHandler

    RateLimitController

    启动微服务后再调用一次

    http://localhost:8401/rateLimit/customerBlockHandler

    Sentinel控制台配置

    测试后我们的自定义出来了

    进一步说明

    更多注解说明

    https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81

    多说一句

    Sentinel主要有三个核心Api

    • sphU定义资源
    • Tracer定义统计
    • ContextUtil定义了上下文

    服务熔断功能

    sentinel整合ribbon+openFeign+fallback

    Ribbon系列

    启动nacos和sentinel

    提供者9003/9004

    新建cloudalibaba-provider-payment9003/9004

    POM

    <dependencies>
        <!-- SpringCloud ailibaba nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- SpringCloud ailibaba sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.eiletxie.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <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>

    YML

    server:
      port: 9003
    
    spring:
      application:
        name: nacos-payment-provider
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'

    主启动

    @SpringBootApplication
    @EnableDiscoveryClient
    public class PaymentMain9003 {
    
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain9003.class,args);
        }
    }

    业务类

    @RestController
    public class PaymentController {
    
        @Value("${server.port}")
        private  String serverPort;
    
        public static HashMap<Long, Payment > map = new HashMap<>();
        static {
            map.put(1L,new Payment(1L,"1111"));
            map.put(1L,new Payment(2L,"2222"));
            map.put(1L,new Payment(3L,"3333"));
        }
    
    
        @GetMapping(value = "/paymentSQL/{id}")
        public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
            Payment payment = map.get(id);
            CommonResult<Payment> result = new CommonResult<>(200,"from mysql,serverPort: " + serverPort,payment);
            return result;
        }
    }

    测试地址

    http://localhost:9003/paymentSQ/

    消费者84

    新建cloudalibaba-consumer-nacos-order84

    POM

    与提供者pom一致

    YML

    server:
      port: 84
    
    spring:
      application:
        name: nacos-order-consumer
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
        sentinel:
          transport:
            dashboard: localhost:8080
            port: 8719
    
    #消费者将去访问的微服务名称
    server-url:
      nacos-user-service: http://nacos-payment-provider

    主启动

    @EnableDiscoveryClient
    @SpringBootApplication
    public class OrderMain84 {
    
        public static void main(String[] args) {
            SpringApplication.run(OrderMain84.class,args);
        }
    }

    业务类

    ApplicationContextConfig
    @Configuration
    public class ApplicationContextConfig {
    
        @Bean
        @LoadBalanced
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
    CircleBreakerController
    public class CircleBreakerController {
    
        public static  final  String SERVICE_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(SERVICE_URL + "/paymentSQL/" + id,CommonResult.class,id);
    
            if(id == 4){
                throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");
            }else if(result.getData() == null) {
                throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
            }
    
            return  result;
        }
    }
    修改后请重启微服务
    • 热部署对java代码级生效及时
    • 对@SentinelResource注解内属性,有时效果不好
    目的
    • fallback管运行异常
    • blockHandler管配置违规
    测试地址

    http://localhost:84/consumer/fallback/3

    没有任何配置

    直接给用户error界面。,,不太友好

    只配置fallback
      @RequestMapping("/consumer/fallback/{id}")
    //    @SentinelResource(value = "fallback")
        @SentinelResource(value = "fallback",fallback = "handlerFallback")
        public CommonResult<Payment> fallback(@PathVariable Long id) {
            CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id,CommonResult.class,id);
    
            if(id == 4){
                throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");
            }else if(result.getData() == null) {
                throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
            }
    
            return  result;
        }
    
    
        public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
            Payment payment = new Payment(id,"null");
            return new CommonResult(444,"异常handlerFallback,exception内容: " + e.getMessage(), payment);
        }

    结果

    只配置blockHandler

    编码

      @RequestMapping("/consumer/fallback/{id}")
    //    @SentinelResource(value = "fallback")
    //    @SentinelResource(value = "fallback",fallback =
        @SentinelResource(value = "fallback",blockHandler = "blockHandler")
        public CommonResult<Payment> fallback(@PathVariable Long id) {
            CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id,CommonResult.class,id);
    
            if(id == 4){
                throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");
            }else if(result.getData() == null) {
                throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
            }
    
            return  result;
        }
     
    public CommonResult blockHandler(@PathVariable Long id,BlockException e) {
        Payment payment = new Payment(id,"null");
        return new CommonResult(444,"blockHandler-sentinel 限流,BlockException: " + e.getMessage(), payment);
    }

    记得配置一个qps的控制

    结果

    同时配置fallback和blockHandler

    异常忽略

    Feign系列

    修改84模块

    • 84消费者调用提供者9003
    • Feign组件一般是消费端

    POM

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

    YML

    业务类

    • 带@FeignClient注解的业务接口
    • fallback = PaymentFallbackService.class
    • Controller

    主启动

    添加@EnableFeignClients启动Feign的功能

    http://localhost:84/consumer/paymentSQL/2

    测试84调用9003,此时故意关闭9003微服务提供者,看84消费自动降级,不会被耗死

    熔断框架比较

    Sentinel Hystrix resilience4j
    隔离策略 信号量隔离(并发线程数限流) 线程池隔离/信号量隔离 信号量隔离
    熔断降级策略 基于响应时间、异常比率、异常数 基于异常比率 基于异常比率、响应时间
    实时统计实现 滑动窗口(LeapArray) 滑动窗口(基于RxJava) Ring Bit Buffer
    动态规则配置 支持多种数据源 支持多种数据源 有限支持
    扩展性 多个扩展点 插件的形式 接口的形式
    基于注解的支持 支持 支持 支持
    限流 基于QPS.支持基于调用关系的限流 有限的支持 Rate Limiter

    规则持久化

    是什么

    一旦我们重启应用,sentinel规则消失,生产环境需要将配置规则进行持久化

    怎么玩

    将限流规则持久进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看得到,只要Nacos里面的配置不删除,针对8401上的流控规则持续有效

    步骤

    修改cloudalibaba-sentinel-server8401

    POM

    <!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>

    YML

    server:
      port: 8401
    
    spring:
      application:
        name: cloudalibaba-sentinal-service
      cloud:
        nacos:
          discovery:
            #Nacos服务注册中心地址
            server-addr: localhost:8848
        sentinel:
          transport:
            #配置Sentin dashboard地址
            dashboard: localhost:8080
            # 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口
            port: 8719
          datasource:
            ds1:
              nacos:
                server-addr: localhost:8848
                dataId: cloudalibaba-sentinel-service
                groupId: DEFAULT_GROUP
                data-type: json
                rule-type: flow
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'
     
     feign:
      sentinel:
        enabled: true #激活Sentinel 对Feign的支持

    添加Nacos业务规则配置

    内容解析

    [
        {
            "resource": "/rateLimit/byUrl",
            "limitApp": "default",
            "grade": 1,
            "count": 1,
            "strategy": 0,
            "controlBehavior": 0,
            "clusterMode": false
        }
    ]

    这一步的作用是每次消费者微服务启动时在nacos中定义sentinel的流控规则,从而做到持久化的效果

    启动8401刷新sentinel发现业务规则变了

    快速访问测试接口

    http://localhost:8401/rateLimit/byUrl
    默认

    停止8401再看sentinel

    重新启动8401再看sentinel

    • 咋一看还是没有了,稍等一会儿
    • 多次调用
      http://localhost:8401/rateLimit/byUrl
    • 重新配置出现了,持久化验证通过
    后海有树的院子,夏代有工的玉,此时此刻的云,二十来岁的你。——《可遇不可求的事》

    笔者将不定期更新【考研或就业】的专业相关知识以及自身理解,希望大家能【关注】我。
    如果觉得对您有用,请点击左下角的【点赞】按钮,给我一些鼓励,谢谢!
    如果有更好的理解或建议,请在【评论】中写出,我会及时修改,谢谢啦!
    关注
    评论
    收藏
    Top
  • 相关阅读:
    【爬虫】新闻统计
    python pip 出错 "Can't connect to HTTPS URL because the SSL module is not available.
    【sql server复制】sql server复制【最佳实践】
    mybase个人数据库笔记工具下载
    sqlserver脱机时ldf文件丢失重建
    sqlserver CTE定位点类型和递归部分的类型不匹配
    mysql物理存储(页区段)
    sql server update与delete引发的死锁
    Linux学习笔记(19)linux定时任务(crontab)
    mysql工具生态
  • 原文地址:https://www.cnblogs.com/blknemo/p/13526507.html
Copyright © 2011-2022 走看看