场景
SpringCloudAlibaba中使用Nacos实现服务注册与发现(从实例入手):
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/125177618
SpringCloudAlibaba中使用Nacos实现配置中心和配置动态刷新:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/125181154
在上面实现的Nacos作为服务注册中心与配置中心之后,学习Sentinel作为流量控制的使用。
Sentinel
是什么
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
特性
丰富的应用场景:
Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
完备的实时监控:
Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
广泛的开源生态:
Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
完善的 SPI 扩展机制:
Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel分为两个部分
核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
官网
https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
教程
下载
https://github.com/alibaba/Sentinel/releases
直接点击jar包进行下载
下载之后直接运行jar包
java -jar sentinel-dashboard-1.8.4.jar
启动成功之后访问8080端口
默认用户名密码都是sentinel,登录成功之后
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。
实现
1、搭建演示工程,参考上面新建模块的流程,新建模块cloudalibaba-sentinel-service8401
修改pom文件,添加sentinel依赖
<!--SpringCloud ailibaba sentinel --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
除此之外,还需要添加nacos以及其它依赖,完整pom文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>SpringCloudDemo</artifactId> <groupId>com.badao</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloudalibaba-sentinel-service8401</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <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> <!-- SpringBoot整合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.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>
新建并修改application.yml配置文件,配置nacos地址和sentinel的地址等
server: port: 8401 spring: application: name: cloudalibaba-sentinel-service cloud: nacos: discovery: server-addr: localhost:8848 #Nacos服务注册中心地址 sentinel: transport: dashboard: localhost:8080 #配置Sentinel dashboard地址 #默认8719端口,如果被占用则自动从8719开始依次+1扫描,直至找到被占用的端口 port: 8719 management: endpoints: web: exposure: include: '*'
新建主启动类并添加注解@EnableDiscoveryClient
package com.badao.springcloudalibabademo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class MainApp8401 { public static void main(String[] args) { SpringApplication.run(MainApp8401.class, args); } }
新建Controller,并新增两个简单的接口
package com.badao.springcloudalibabademo.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController @Slf4j public class FlowLimitController { @GetMapping("/testA") public String testA() { return "------testA"; } @GetMapping("/testB") public String testB() { log.info(Thread.currentThread().getName()+"\t"+"...testB"); return "------testB"; } }
2、启动Nacos,启动上面的sentinel,再启动8401服务
Sentinel采用的是懒加载的方式,所以需要调用一次接口才会在Sentinel中有数据。
所以在浏览器中多次调用接口
然后访问sentinel的dashboard
就可以实时监控刚才调用的服务了。
3、点击流控规则,再点击新增流控规则,可以看到如下界面
说明:
资源名:唯一名称,默认请求路径。
针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认defaule(不区分来源)
阈值类型/单机阈值:
QPS:每秒终的请求数量,当调用该api的QPS达到阈值的时候,进行限流。
线程数:当调用该api的线程数达到阈值的时候,进行限流。
流控模式:
直接:api达到限流条件时,直接限流。
关联:当关联的资源达到阈值时,就限流自己。
链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】
流控效果:
快速失败:直接失败,抛异常。
Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值。
排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效。
这块说明可以具体参考官方说明文档:
https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
4、阈值类型为QPS演示
在上面的新建流控规则,选择QPS,高级选项中保持默认,设置单机阈值为1,
单机阈值为1即代表一秒以内如果请求次数大于1次则进行限流。
访问testA接口,先慢频率访问,后快速访问
5、并发线程数阈值类型演示
继续上面的将阈值类型修改为并发线程数,并设置单机阈值为1。
此时为了演示出效果,需要修改接口有个延迟返回效果,避免返回过快
@GetMapping("/testA") public String testA() { //暂停毫秒 try{ TimeUnit.MILLISECONDS.sleep(800); }catch (InterruptedException e){e.printStackTrace();} return "------testA"; }
让testA接口延迟0.8秒返回。
此时当并发量过大时就会触发限流
6、流控模式为关联效果
要实现当关联资源/testB的qps阈值超过1时,就限流/testA
修改流控规则如下
为了模拟/testB多次请求,可以借助于Jmeter或者Postman用来做定时模拟请求
这里用postman-新建Collections-新建请求request为testB-配置请求20次,每隔300毫秒请求一次。
在模拟请求testB的过程中,此时再请求testA就会触发限流。
7、流控效果Warm Up效果演示
Warm Up
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。
当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。
通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
概述
当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲的状态,
我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的的。
这个场景主要用于启动需要额外开销的场景,例如建立数据库连接等。
它的实现是在 Guava 的算法的基础上实现的。然而,和 Guava 的场景不同,Guava 的场景主要用于调节请求的间隔,
即 Leaky Bucket,而 Sentinel 则主要用于控制每秒的 QPS,即我们满足每秒通过的 QPS 即可,我们不需要关注每个请求的间隔,
换言之,我们更像一个 Token Bucket。
我们用桶里剩余的令牌来量化系统的使用率。假设系统每秒的处理能力为 b,系统每处理一个请求,就从桶中取走一个令牌;
每秒这个令牌桶会自动掉落b个令牌。令牌桶越满,则说明系统的利用率越低;当令牌桶里的令牌高于某个阈值之后,我们称之为令牌桶"饱和"。
当令牌桶饱和的时候,基于 Guava 的计算上,我们可以推出下面两个公式:
rate(c)=m*c+ coldrate
其中,rate 为当前请求和上一个请求的间隔时间,而 rate 是和令牌桶中的高于阈值的令牌数量成线形关系的。
cold rate 则为当桶满的时候,请求和请求的最大间隔。通常是 coldFactor * rate(stable)。
官网详细文档
https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8
公式讲解
阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值。
默认coldFactor为3,即请求QPS从threshols/3开始,经预热时长逐渐升至设定的QPS阈值。
举例
系统初始化的阈值为6/3=2,即阈值刚开始为2;然后过了4秒之后阈值才慢慢升高恢复至6
此时再高频访问testA,一开始阈值是2,所以会被限流,在4秒之后,阈值变为6,则可以正常访问
应用场景:
秒杀系统在开启的瞬间,会有很多流量上来,就有可能会把系统打死,预热方式就是慢慢的把阈值增长到设置的阈值。
8、排队等待演示效果
匀速排队:让请求以均匀的速度通过,阈值类型必须设置成QPS,否则无效。
这种方式主要用于处理间隔性突发的流量,例如消息队列。例如在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,
我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
设置含义:/testA每秒1次请求,超过的话就排队等待,等待的超时时间为20000毫秒。
编辑上面的流控规则为排队等待
修改testA的接口使其输出一些信息
@GetMapping("/testA") public String testA() { log.info(Thread.currentThread().getName()+"\t"+"...testA"); return "------testA"; }
然后再用postman发起20次请求,每次请求间隔200毫秒,可以看到testA接口仍然是1秒响应一次