Spring cloud 限流的多种方式
在频繁的网络请求时,服务有时候也会受到很大的压力,尤其是那种网络攻击,非法的。这样的情形有时候需要作一些限制。本文主要介绍了两种限流方法,感兴趣的可以了解一下
目录
-
一、实战基于 Spring cloud Gateway 的限流
-
二、基于阿里开源限流神器:Sentinel
在频繁的网络请求时,服务有时候也会受到很大的压力,尤其是那种网络攻击,非法的。这样的情形有时候需要作一些限制。例如:限制对方的请求,这种限制可以有几个依据:请求IP、用户唯一标识、请求的接口地址等等。
当前限流的方式也很多:Spring cloud 中在网关本身自带限流的一些功能,基于 redis 来做的。同时,阿里也开源了一款:限流神器 Sentinel。今天我们主要围绕这两块来实战微服务的限流机制。
首先讲 Spring cloud 原生的限流功能,因为限流可以是对每个服务进行限流,也可以对于网关统一作限流处理。
一、实战基于 Spring cloud Gateway 的限流
pom.xml引入依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis-reactive</artifactId></dependency>
其基础是基于redis,所以:
spring:
application:
name: gateway-service
redis: #redis相关配置
database: 8
host: 10.12.15.5
port: 6379
password: 123456 #有密码时设置
jedis:
pool:
max-active: 8
max-idle: 8
min-idle: 0
timeout: 10000ms
接下来需要注入限流策略的 bean:
@Primary@Bean(value = "ipKeyResolver")KeyResolver ipKeyResolver() {return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());//return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());//return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());}@Bean(value = "apiKeyResolver")KeyResolver apiKeyResolver() {return exchange -> Mono.just(exchange.getRequest().getPath().value());}@Bean(value = "userKeyResolver")KeyResolver userKeyResolver() {return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));}
这里引入ipKeyResolver、apiKeyResolver、userKeyResolver三种策略,可以利用注解 @Primary 来决定其中一个被使用。
注入bean后,需要在配置中备用:
spring:
application:
name: gateway-service
redis: #redis相关配置
database: 8
host: 10.12.15.5
port: 6379
password: 123456 #有密码时设置
jedis:
pool:
max-active: 8
max-idle: 8
min-idle: 0
timeout: 10000ms
后面是限流的主要配置:
spring
cloud:
gateway:
routes: #路由配置:参数为一个List
- id: cas-server #唯一标识
uri: lb://cas-server-service #转发的地址,写服务名称
order: -1
predicates:
- Path=/cas-server/** #判断匹配条件,即地址带有/ribbon/**的请求,会转发至lb:cas-server-service
filters:
- StripPrefix=1 #去掉Path前缀,参数为1代表去掉/ribbon
- name: RequestRateLimiter #基于redis的Gateway的自身限流
args:
redis-rate-limiter.replenishRate: 1 # 允许用户每秒处理多少个请求
redis-rate-limiter.burstCapacity: 3 # 令牌桶的容量,允许在一秒钟内完成的最大请求数
key-resolver: "#{@ipKeyResolver}" #SPEL表达式取的对应的bean
- id: admin-web
uri: lb://admin-web-service
order: -1
predicates:
- Path=/admin-web/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1 # 允许用户每秒处理多少个请求
redis-rate-limiter.burstCapacity: 3 # 令牌桶的容量,允许在一秒钟内完成的最大请求数
key-resolver: "#{@ipKeyResolver}" #SPEL表达式取的对应的bean
这里是在原有的路由基础上加入 RequestRateLimiter限流过滤器,包括三个参数:
- name: RequestRateLimiter #基于redis的Gateway的自身限流
args:
redis-rate-limiter.replenishRate: 3 #允许用户每秒处理多少个请求
redis-rate-limiter.burstCapacity: 5 #令牌桶的容量,允许在一秒钟内完成的最大请求数
key-resolver: "#{@ipKeyResolver}" #SPEL表达式取的对应的bean
-
其中 replenishRate,其含义表示允许每秒处理请求数;
-
burstCapacity 表示允许在一秒内处理的最大请求数;
-
key-resolver 这里采用请求 IP 限流,利用SPEL 表达式取对应的 bean
写一个小脚本来压测一下:
for i in $(seq 1 30000); do echo $(expr $i \\* 3 + 1);curl -i -H "Accept: application/json" -H "Authorization:bearer b064d95b-af3f-4053-a980-377c63ab3413" -X GET http://10.10.15.5:5556/order-service/api/order/getUserInfo;donefor i in $(seq 1 30000); do echo $(expr $i \\* 3 + 1);curl -i -H "Accept: application/json" -H "Authorization:bearer b064d95b-af3f-4053-a980-377c63ab3413" -X GET http://10.10.15.5:5556/admin-web/api/user/getCurrentUser;done
上面两个脚本分别对2个服务进行压测,打印结果:
{"message":{"status":200,"code":0,"message":"success"},"data":"{\"message\":{\"status\":200,\"code\":0,\"message\":\"get user success\"},\"data\":{\"id\":23,\"isAdmin\":1,\"userId\":\"fbb18810-e980-428c-932f-848f3b9e7c84\",\"userType\":\"super_admin\",\"username\":\"admin\",\"realName\":\"super_admin\",\"password\":\"$2a$10$89AqlYKlnsTpNmWcCMvgluRFQ/6MLK1k/nkBpz.Lw6Exh.WMQFH6W\",\"phone\":null,\"email\":null,\"createBy\":\"admin\",\"createTime\":1573119753172,\"updateBy\":\"admin\",\"updateTime\":1573119753172,\"loginTime\":null,\"expireTime\":null,\"remarks\":\"super_admin\",\"delFlag\":0,\"loginType\":null}}"}ex
在用测试工具Jmeter在同一秒内多次请求后:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Remaining: 0
X-RateLimit-Burst-Capacity: 3
X-RateLimit-Replenish-Rate: 1
content-length: 0
expr: syntax error
HTTP/1.1 429 Too Many Requests
X-RateLimit-Remaining: 0
X-RateLimit-Burst-Capacity: 3
X-RateLimit-Replenish-Rate: 1
content-length: 0
expr: syntax error
从上面可以看到,执行后,会出现调用失败的情况,状态变为429 (Too Many Requests) 。
二、基于阿里开源限流神器:Sentinel
首先引入依赖:
<!--基于 阿里的sentinel作限流 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>
在配置文件 application.yaml 文件中配置,需要新增2个配置:
spring:
application:
name: admin-web
cloud:
kubernetes:
discovery:
all-namespaces: true
sentinel:
eager: true #取消Sentinel控制台的懒加载
transport:
dashboard: 10.12.15.2:8080 #sentinel的Dashboard地址
port: 8719 #是sentinel应用端和控制台通信端口
heartbeat-interval-ms: 500 #心跳时间
scg:
fallback: #scg.fallback为sentinel限流后的响应配置
mode: response
response-status: 455
response-body: 已被限流
其中,这里面配置了一个服务:spring.cloud.sentinel.transport.dashboard,配置的是 sentinel 的 Dashboard 地址。同时 spring.cloud.sentinel.transport.port 这个端口配置会在应用对应的机器上启动一个Http Server,该 Server 会与 Sentinel 控制台做交互。
Sentinel 默认为所有的 HTTP 服务提供限流埋点,上面配置完成后自动完成所有埋点,只需要控制配置限流规则即可。
这里我们讲下通过注解来给指定接口函数加上限流埋点,写一个RestController,在接口函数上加上注解
@SentinelResource:@GetMapping(value = "/getToken")@SentinelResource("getToken")public Response<Object> getToken(Authentication authentication){//Authentication authentication = SecurityContextHolder.getContext().getAuthentication();authentication.getCredentials();OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)authentication.getDetails();String token = details.getTokenValue();return Response.ok(200, 0, "get token success", token);}
以上代码部分完成了,接下来先安装SentinelDashBoard,Sentinel DashBoard下载地址:github.com/alibaba/Sentinel/releases。
下载完成后,命令启动:
java -jar sentinel-dashboard-1.6.2.jar
默认启动端口为8080,访问 IP:8080,就可以显示 Sentinel 的登录界面,用户名与密码均为sentinel。登录 Dashboard 成功后,多次访问接口"/getToken",可以在 Dashboard 看到相应数据,这里不展示了。接下来可以设置接口的限流功能,在 “+流控” 按钮点击打开设置界面,设置阈值类型为 qps,单机阈值为5。
浏览器重复请求 http://10.10.15.5:5556/admin-web/api/user/getToken 如果超过阀值就会出现如下界面信息:
Blocked by Sentinel (flow limiting)
此时,就看到Sentinel 限流起作用了,可以加上 spring.cloud.sentinel.scg.fallback 为sentinel 限流后的响应配置,亦可自定义限流异常信息:
@GetMapping(value = "/getToken")@SentinelResource(value = "getToken", blockHandler = "handleSentinelException", blockHandlerClass = {MySentinelException.class}))public Response<Object> getToken(Authentication authentication){//Authentication authentication = SecurityContextHolder.getContext().getAuthentication();authentication.getCredentials();OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)authentication.getDetails();String token = details.getTokenValue();return Response.ok(200, 0, "get token success", token);}public class MySentinelException {public static Response<Object> handleSentinelException(BlockException e) {Map<String,Object> map=new HashMap<>();logger.info("Oops: " + ex.getClass().getCanonicalName());return Response.ok(200, -8, "通过注解 @SentinelResource 配置限流埋点并自定义限流后的处理逻辑", null);}}
这里讲下注解 @SentinelResource 包含以下属性:
-
value:资源名称,必需项;
-
entryType:入口类型,可选项(默认为 EntryType.OUT);
-
blockHandler:blockHandlerClass中对应的异常处理方法名,参数类型和返回值必须和原方法一致;
-
blockHandlerClass:自定义限流逻辑处理类
Sentinel 限流逻辑处理完毕了,但每次服务重启后,之前配置的限流规则就会被清空。因为是内存形式的规则对象。所以下面就讲下用 Sentinel 的一个特性 ReadableDataSource 获取文件、数据库或者配置中心设置限流规则,目前支持 Apollo、Nacos、ZK 配置来管理。
首先回忆一下,一条限流规则主要由下面几个因素组成:
-
resource:资源名,即限流规则的作用对象,即为注解 @SentinelResource 的value;
-
count:限流阈值;grade:限流阈值类型(QPS 或并发线程数);
-
limitApp:流控针对的调用来源,若为 default 则不区分调用来源;
-
strategy:基于调用关系的限流策略;
-
controlBehavior:流量控制效果(直接拒绝、排队等待、匀速器模式)
理解了意思,接下来通过文件来配置:
#通过文件读取限流规则
spring.cloud.sentinel.datasource.ds1.file.file=classpath:flowrule.json
spring.cloud.sentinel.datasource.ds1.file.data-type=json
spring.cloud.sentinel.datasource.ds1.file.rule-type=flow
在resources新建一个文件,比如 flowrule.json 添加限流规则:
[
{
"resource": "getToken",
"count": 1,
"controlBehavior": 0,
"grade": 1,
"limitApp": "default",
"strategy": 0
},
{
"resource": "resource",
"count": 1,
"controlBehavior": 0,
"grade": 1,
"limitApp": "default",
"strategy": 0
}
]
重新启动项目,出现如下日志说明成功:
DataSource ds1-sentinel-file-datasource start to loadConfig
DataSource ds1-sentinel-file-datasource load 2 FlowRule
如果采用 Nacos 作为配置获取限流规则,可在文件中加如下配置:
spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: 10.10.15.5:8848
discovery:
server-addr: 10.10.15.5:8848
sentinel:
eager: true
transport:
dashboard: 10.10.15.5:8080
datasource:
ds1:
nacos:
server-addr: 10.10.15.5:8848
dataId: ${spring.application.name}-flow-rules
data-type: json
rule-type: flow
相关文章:
Spring cloud 限流的多种方式
在频繁的网络请求时,服务有时候也会受到很大的压力,尤其是那种网络攻击,非法的。这样的情形有时候需要作一些限制。本文主要介绍了两种限流方法,感兴趣的可以了解一下 目录 一、实战基于 Spring cloud Gateway 的限流 二、基于阿…...

Linux命令·top
top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。下面详细介绍它的使用方法。top是一个动态显示过程,即可以通过用户按键来不断刷新当前状态.如果在前台执行该命令,它将独占前台,直到用户终止…...

springmvc之系列文章
springmvc之编程步骤 springmvc初始化过程 用WebServlet和WebFilter干掉web.xml 没有web.xml怎么写web程序 一次GET请求在springmvc中是的处理流程 springMVC的handler都有哪些类型 springmvc主要组件简单介绍 springmvc 的Servlet WebApplicationContext springmvc 的…...
Matlab实现深度学习(附上完整仿真源码)
文章目录简单案例完整仿真代码下载简单案例 深度学习是一种能够自动学习和提取数据特征的机器学习方法,它已经在图像识别、语音识别、自然语言处理等领域取得了显著的成果。而Matlab作为一个强大的数学计算工具,也提供了丰富的深度学习工具箱࿰…...
我的谷歌书签
Form 表单 | Element Plusa Vue 3 based component library for designers and developershttps://element-plus.gitee.io/zh-CN/component/form.html#%E5%AF%B9%E9%BD%90%E6%96%B9%E5%BC%8F three.js exampleshttp://www.yanhuangxueyuan.com/threejs/examples/#software_geo…...

day3 数据库技术考点汇总
一、重点知识点 基本概念:三级模式-两级映像、数据库设计数据库模型:E-R模型、关系模型、关系代数(结合SQL语言)规范化:函数依赖、健与约束、范式、模式分解事务并发:并发三种问题、三级封锁协议数据库新技…...

学剪辑难吗 如何使用会声会影2023做剪辑视频
很多剪辑初学者都问过一个问题,学剪辑难吗?其实不论学什么,只要用心学都不难,今天我们就来讲讲如何学做剪辑视频,感兴趣的小伙伴们不要走开!一、学剪辑难吗 其实学剪辑并不是件难事,但是需要掌握…...
django学习日记
1、虚拟环境 virtualenv "加虚拟环境名字" 在当前目录下创建一个虚拟环境 进入虚拟环境执行activate进入该虚拟环境,再执行deactivate退出虚拟环境 安装一个包来管理虚拟环境,每次创建虚拟环境都放到同一位置,以及在任意位置都可…...

在线教学视频课程如何防止学员挂机?
阿酷TONY / 2023-3-31 / 长沙 / 原创 / 要不?交个朋友吧? 在线教学视频课程如何防止学员挂机?siri:这是个有意思的问题哈~~~在线教育、在线企业培训机构通常是如何处理的呢? 答:在视频播放过程中,弹出问题…...

【Redis】安装配置
文章目录Redis简介Redis版本迭代Redis安装配置官网地址操作系统环境基础查看本地redis版本修改配置文件docker容器安装redis测试linux版Redis简介 简介 与传统数据库关系(mysql),Redis是key-value数据库(NoSQL一种),mysql是关系数据库。Redis数据操作主要…...

ChatGPT批量生成文章-ChatGPT文章生成器
ChatGPT:一键批量生成高质量文章,提高生产效率! 随着信息爆炸的时代,文本生产成为了各个行业必不可少的一部分。但面对高强度的生产需求,人力资源却难以跟上步伐。现在,我们有一款基于人工智能和自然语言处…...
Linux命令 ——sed
介绍 sed 是一种流式文本编辑器,常用于在 Unix 和类 Unix 系统中对文本进行处理。它可以将文本从标准输入或文件中读取,对其进行修改,然后将修改后的文本输出到标准输出或文件中。sed 是 “stream editor” 的缩写。 语法 sed 的基本语法为…...
C++常用字符串string方法
文章目录字符串string操作方法1. 类方法使用示例2. 头文件cstring方法使用示例字符串string操作方法 1. 类方法 在C中,引入string.h头文件可以使用C语言中的字符串操作函数。然而,C提供了一个更加方便的字符串类string,不需要引入string.h头…...

XML树结构和语法
文章目录一、XML 树结构二、XML 语法规则总结一、XML 树结构 XML 树结构 XML 文档形成了一种树结构,它从"根部"开始,然后扩展到"枝叶"。 一个 XML 文档实例 XML 文档使用简单的具有自我描述性的语法: <?xml vers…...
【Qt】Qt单元测试详解(四):Google Test 断言
1、创建测试工程 【Qt】Qt单元测试详解(一):通过QtCreator创建测试工程 2、添加测试代码 2.1 默认生成的代码 1)项目工程pro include(gtest_dependency.pri)TEMPLATE = app CONFIG += console c++14 CONFIG -= app_bundle CONFIG += thread CONFIG -= qtHEADERS += \t…...
句柄和指针的区别
句柄和指针都是一种数据结构,都常用于访问内存,下面介绍他们的一些不同点。 1 数据结构类型不同 指针的数据类型是无符号整数,占用4或8个字节(在32位和64位系统中),它就像一个变量一样,这个变量…...
Linux 网络编程学习笔记——十四、多线程编程
目录 早期 Linux 不支持线程,直到 1996 年,Xavier Leroy 等人才开发出第一个基本符合 POSIX 标准的线程库 LinuxThreads 。但 LinuxThreads 效率低而且问题很多。自内核 2.6 开始,Linux 才真正提供内核级的线程支持,并有两个组织…...
JS 获取时区
JS 获取时区 啥是时区? 时区是地球上的区域使用同一个时间定义。以前,人们通过观察太阳的外置(时角)决定时间,这就使得不同经度的地方的时间各有不同,为了统一使用同一个时间,就引入了时区的概…...

【0183】PG内核客户端认证之将读取的token创建HbaToken(3 - 1)
文章目录 1. 将HbaToken添加到List指针变(续)1.1 示意图的一些问题1.2 修复后的示意图2. 将一行HBA所有字段添加到current_line2.1 代码实现2.2 示意图实现1. 将HbaToken添加到List指针变(续) 在【0182】PG内核客户端认证之将读取的token创建HbaToken(3) 一文中的第3节中…...

别把 OpenAI 太当回事,它远未达到替换前端的地步
最近几个月,我和很多初入行的开发人员交谈,他们对AI越来越感到焦虑。他们看到像GPT-4这样的工具展示的越来越令人印象深刻的演示,担心等他们掌握了HTML/CSS/JS,就没有任何工作机会了。这种情绪现在在Twitter上广泛存在:…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...