SpringCloud Alibaba Sentinel 流量控制 - 程序配制方式实现

本文主要是介绍SpringCloud Alibaba Sentinel 流量控制 - 程序配制方式实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、Sentinel

前面在讲解Hystrix 服务保护框架的时候就,提到了AibabaCloud的Sentinel 框架,我们都知道Hystrix 已经不再维护了,那我们该怎么办呢,现在Sentinel 就是我们必须入手的服务保护框架。如果想了解Hystrix可以参考下面我的博客:

Hystrix 服务降级:https://blog.csdn.net/qq_43692950/article/details/121996806

Hystrix 服务熔断、限流:https://blog.csdn.net/qq_43692950/article/details/121998205

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定性,下面来了解下Sentinel的功能:

Sentinel 的主要功能

  • 流量控制

流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示:
在这里插入图片描述
流量控制有以下几个角度:

资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
运行指标,例如 QPS、线程池、系统负载等;
控制的效果,例如直接限流、冷启动、排队等。
Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。

  • 熔断降级

除了流量控制以外,降低调用链路中的不稳定资源也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积。这个问题和 Hystrix 里面描述的问题是一样的。
在这里插入图片描述
Sentinel 和 Hystrix 的原则是一致的: 当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常比例升高的时候,则对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。

  • 系统负载保护

Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。

针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

二、通过程序配制的方式实现 - 流量控制

了解Sentinal的应该都知道,Sentinal提供了可视化的控制台,可以方便的进行规则的修改,但我们这边为什么还是讲解程序配制的方式呢,因为简单的东西更容易让人忽视其中的原理,通过程序配制 再到 控制台,可以更加灵活的对控制台进行掌控。

下面我们就开始Sentinel 的使用吧!

首先新建SpringBoot Module ,在pom 中引入以下依赖:
AlibabaCloud 我采用的目前比较新的2.2.6版本的,可以根据自己的需要引用相应版本。

<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</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>com.bxc</groupId><artifactId>common</artifactId><version>0.0.1-SNAPSHOT</version>
</dependency>

其中common为我们前面几篇博客中写的公共的module,主要封装了controller的返回,不引用该包,将controller的返回指定为自己的即可。

修改配制文件application.yml

server:port: 8080spring:application:name: cloudalibaba-sentinelcloud:nacos:discovery:server-addr: 192.168.40.130:8848 management:endpoints:web:exposure:include: '*'

主启动类:

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

到这里基本的环境就已经搭建好了,下面可以新建流控规则了:

基于QPS的限流控制
@Configuration
public class SentinalConfig {@PostConstructpublic void initLimitFlow() {FlowRule rule1 = new FlowRule();rule1.setResource("qpsLimit");// 资源名rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);// QPS限流rule1.setCount(2); // 阈值,QPS控制在2以内,每秒访问两次, WARM_UP: 开始状态 QPS为 1/3,经预热时间上升为count数rule1.setLimitApp("default"); //针对来源rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);List<FlowRule> rules = new ArrayList<FlowRule>();rules.add(rule1);FlowRuleManager.loadRules(rules);}
}

指定为QPS的限流方式,限制每秒只允许请求两个进入,这个算法是采用的令牌桶的方式,拒绝方式指定的直接拒绝,可以选择:

  • CONTROL_BEHAVIOR_WARM_UP = 1; 预热/冷启动方式,当系统长期处于低并发的情况下,流量突然增加到qps的最高峰值,可能会造成系统的瞬间流量过大把系统压垮。所以warmup,相当于处理请求的数量是缓慢增加,经过一段时间以后,到达系统处理请求个数的最大值
  • CONTROL_BEHAVIOR_RATE_LIMITER = 2; 匀速排队方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法
  • CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER = 3; 匀速排队预热/冷启动方式

既然上面已经写好了流控规则,那下面就要和具体某个接口来关联起来进行限制了,编写测试接口:

@RestController
@RequestMapping("/limit")
public class LimitController {@GetMapping("/qpsLimit")public ResponseTemplate qpsLimit() {try (Entry entry = SphU.entry("qpsLimit")) {return ResSuccessTemplate.builder().build();} catch (BlockException e) {return ResFailTemplate.builder().message("接口限流!").build();}}
}

在接口中我们使用SphU工具尝试获取qpsLimit资源,这个资源名就是上面创建规则时指定的,如果获取不到会抛出BlockException 错误,因此我们捕获BlockException 错误就表示限流降级了,并返回相应提示。

下面可以在浏览器快速访问:http://localhost:8080/limit/qpsLimit

在这里插入图片描述
可以看到已经实现限流的效果了,现在是通过捕获BlockException异常的方式来进入降级的处理,如果有几十上百个接口,每个都try{} catch (){} 也是个辛苦的事情,为此Sentinel 提供了注解的方式调用资源:

@SentinelResource(value = "qpsLimit",blockHandler = "blockHandler")
@GetMapping("/qpsLimit2")
public ResponseTemplate qpsLimit2() {return ResSuccessTemplate.builder().build();
}private ResponseTemplate blockHandler(){return ResFailTemplate.builder().message("接口限流!").build();
}

通过blockHandler 指定降级方案,这点和Hystrix 类似。
在浏览器快速调用刷新接口:
在这里插入图片描述
也达到了相同的效果。

基于线程池的限流控制

修改规则:

@PostConstruct
public void initLimitFlow() {FlowRule rule1 = new FlowRule();rule1.setResource("threadLimit");// 资源名rule1.setGrade(RuleConstant.FLOW_GRADE_THREAD);// QPS限流rule1.setCount(2); // 阈值,限制线程并发数为2rule1.setLimitApp("default"); //针对来源rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);List<FlowRule> rules = new ArrayList<FlowRule>();rules.add(rule1);FlowRuleManager.loadRules(rules);
}

其中这里的count 就是线程的并发数了,超过该值就会进入降级:
下面编写一个测试入口:

@SentinelResource(value = "threadLimit",blockHandler = "blockHandler")@GetMapping("/threadLimit")public ResponseTemplate qpsLimit3() throws InterruptedException {TimeUnit.SECONDS.sleep(5);System.out.println("qpsLimit3: "+Thread.currentThread().getName());return ResSuccessTemplate.builder().build();}

在接口中做了5秒的延时,以确保可以实现多个线程的效果。

下面在浏览器开三个窗口,以快速访问接口,可以看到,第三个很快结束,并返回限流提示:
在这里插入图片描述

拒绝策略的修改

上面在设置rule1.setControlBehavior的时候都设置的RuleConstant.CONTROL_BEHAVIOR_WARM_UP直接拒绝的策略,达到策略规则直接调用降级方法,其中CONTROL_BEHAVIOR_RATE_LIMITER表示匀速排队方式会严格控制请求通过的间隔时间,相当于同步的方式了,这里我们主要看下CONTROL_BEHAVIOR_WARM_UP模式,预热/冷启动方式。

这种方式只能用于QPS的流控规则,在指定为该规则时,还要指定一个参数warmUpPeriodSec表示预热时间,比如设置QPS为3 ,warmUpPeriodSec 为10的话,则表示在开始请求时QPS为 设置QPS/3 就等于1 ,十秒后将QPS提高至 3,以达到QPS流量平滑的上升。

@PostConstruct
public void initLimitFlow() {FlowRule rule1 = new FlowRule();rule1.setResource("qpsLimit");// 资源名rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);// QPS限流rule1.setCount(3); // 阈值,初始为1,10秒后上升至10rule1.setLimitApp("default"); //针对来源rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);rule1.setWarmUpPeriodSec(10);List<FlowRule> rules = new ArrayList<FlowRule>();rules.add(rule1);FlowRuleManager.loadRules(rules);
}

再次请求接口:http://localhost:8080/limit/qpsLimit
会发现一秒只能访问一次,十秒后发现一秒可以多次了。
在这里插入图片描述

基于热点规则的限流控制

有些时候我们需要对一个接口中的不同参数来进行控制,那么就可以使用Sentinal的热点规则限流策略,可以根据是否有无某个参数,或者某个参数的内容进行控制:

@PostConstruct
public void initLimitFlow() {ParamFlowRule paramFlowRule = new ParamFlowRule();paramFlowRule.setResource("keylimit");//	限流阈值,全局的paramFlowRule.setCount(1);paramFlowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);//热点参数的索引,必填,对应 SphU.entry(xxx, args) 中的参数索引位置paramFlowRule.setParamIdx(0);ParamFlowItem paramFlowItem = new ParamFlowItem().setObject(String.valueOf("1")) //设置热点参数的数值.setClassType(String.class.getName()) //设置热点参数的类型.setCount(10);//单独参数的阈值ParamFlowItem paramFlowItem2 = new ParamFlowItem().setObject(String.valueOf("2")) //设置热点参数的数值.setClassType(String.class.getName()) //设置热点参数的类型.setCount(2);//单独参数的阈值List<ParamFlowItem> paramFlowlist = new ArrayList<>();paramFlowlist.add(paramFlowItem);paramFlowlist.add(paramFlowItem2);paramFlowRule.setParamFlowItemList(paramFlowlist);List<ParamFlowRule> list = new ArrayList<>();list.add(paramFlowRule);ParamFlowRuleManager.loadRules(list);
}

ParamFlowRule 就是用于热点规则的Api,和上面的规则有些变化,需要注意。这里限制对第0个参数进行限制,也就是我们理解的第一个参数,如果第一个参数为1则QPS为10,如果第一个参数为2,则QPS为2,其他值QPS都是1,如果接口有多个参数,第一个参数没传则不符合该规则,没有流控限制,下面编写一个测试接口:

@SentinelResource(value = "keylimit",blockHandler = "blockHandler")@GetMapping("/keyLimit")public ResponseTemplate keyLimit(String a,String b){System.out.println(Thread.currentThread().getName());return ResSuccessTemplate.builder().build();}

浏览器访问:http://localhost:8080/limit/keyLimit?b=1
在这里插入图片描述
会发现无论怎么快速访问都是返回正确结果,因为第一个参数a没有传递,此时是没有流控规则限制的,下面添加上a这个参数:

浏览器访问:http://localhost:8080/limit/keyLimit?a=3

在这里插入图片描述
此时,快速点两下就会进入限流策略,此时QPS为1,将a的参数换为 2,再请求会发现明显进入限流的次数变少了,如果替换为1,则可以发现几乎点不出限流的返回了,因为现在的QPS为10,如果写个脚本并发访问,就可以看到限流的返回。

系统热点限流

通过上面的配制可以发现限制某个接口或多个接口,都需要使用注解或自定义捕获异常的方式,如果相对全局的接口进行控制,总不能所有接口都添加注解吧,因此Sentinel提供了全局的流控配制,如下:

@PostConstruct
public void initLimitFlow() {List<SystemRule> rules = new ArrayList<SystemRule>();SystemRule rule = new SystemRule();//load 触发值,用于触发自适应控制阶段rule.setHighestSystemLoad(3.0);//当前系统的 CPU 使用率(0.0-1.0)rule.setHighestCpuUsage(0.6);//所有入口流量的平均响应时间rule.setAvgRt(10);//所有入口资源的 QPSrule.setQps(20);//入口流量的最大并发数rule.setMaxThread(10);rules.add(rule);SystemRuleManager.loadRules(rules);
}

上面这种全局的配制比较适合网关的配制,在服务的网关处全局控制。

在这里插入图片描述
喜欢的小伙伴可以关注我的个人微信公众号,获取更多学习资料!

这篇关于SpringCloud Alibaba Sentinel 流量控制 - 程序配制方式实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/378769

相关文章

Java中对象的创建和销毁过程详析

《Java中对象的创建和销毁过程详析》:本文主要介绍Java中对象的创建和销毁过程,对象的创建过程包括类加载检查、内存分配、初始化零值内存、设置对象头和执行init方法,对象的销毁过程由垃圾回收机... 目录前言对象的创建过程1. 类加载检查2China编程. 分配内存3. 初始化零值4. 设置对象头5. 执行

SpringBoot整合easy-es的详细过程

《SpringBoot整合easy-es的详细过程》本文介绍了EasyES,一个基于Elasticsearch的ORM框架,旨在简化开发流程并提高效率,EasyES支持SpringBoot框架,并提供... 目录一、easy-es简介二、实现基于Spring Boot框架的应用程序代码1.添加相关依赖2.添

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

SpringBoot中整合RabbitMQ(测试+部署上线最新完整)的过程

《SpringBoot中整合RabbitMQ(测试+部署上线最新完整)的过程》本文详细介绍了如何在虚拟机和宝塔面板中安装RabbitMQ,并使用Java代码实现消息的发送和接收,通过异步通讯,可以优化... 目录一、RabbitMQ安装二、启动RabbitMQ三、javascript编写Java代码1、引入

MySQL8.0设置redo缓存大小的实现

《MySQL8.0设置redo缓存大小的实现》本文主要在MySQL8.0.30及之后版本中使用innodb_redo_log_capacity参数在线更改redo缓存文件大小,下面就来介绍一下,具有一... mysql 8.0.30及之后版本可以使用innodb_redo_log_capacity参数来更改

spring-boot-starter-thymeleaf加载外部html文件方式

《spring-boot-starter-thymeleaf加载外部html文件方式》本文介绍了在SpringMVC中使用Thymeleaf模板引擎加载外部HTML文件的方法,以及在SpringBoo... 目录1.Thymeleaf介绍2.springboot使用thymeleaf2.1.引入spring

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D