API接口限流就是这么简单(api 限流 java实现)
toyiye 2024-09-12 21:01 7 浏览 0 评论
1. 简介
访问速率限制是一种API访问限制的策略。它限制客户端在一定时间内调用 API 的次数。这有助于保护应用程序接口,防止无意或恶意的过度使用。
速率限制通常是通过跟踪 IP 地址或更具体的业务方式(如 API 密钥或访问令牌等方式)来应用于 API 的。作为 API 开发人员,当客户端达到限制时,我们有几种选择:
- 请求排队,直到剩余时间结束(这也是最常用的方式)
- 拒绝请求(HTTP 429 请求过多)
本篇文章将介绍一款开源的组件Bucket4j,该组件提供了强大的限流功能。基于基于令牌桶算法。既可用于独立的 JVM 应用程序,也可用于集群环境。它还通过 JCache(JSR107)规范支持内存或分布式缓存。
令牌桶算法
假设我们有一个 "桶",其容量被定义为可容纳的令牌数量。每当消费者想要访问 API 端点时,就必须从桶中获取一个令牌。如果有令牌,我们就会从数据桶中移除令牌,并接受请求。反之,如果程序桶中没有令牌,我们就会拒绝请求。
在请求消耗令牌(token)的同时,我们也在以某种固定的速度补充令牌。
考虑一个速率限制为每分钟 100 个请求的应用程序接口。我们可以创建一个容量为 100 的水桶,每分钟填充 100 个令牌。如果我们在一分钟内收到 70 个请求,少于可用令牌的数量,那么在下一分钟开始时,我们只需再添加 30 个令牌,就能使水桶达到容量。另一方面,如果我们在 40 秒内用完了所有令牌,我们将等待 20 秒来重新装满令牌桶。
接下来将详细介绍在Spring Boot中如何使用Bucket4j实现限流。
2. 实战案例
2.1 环境准备
引入依赖
<dependency>
<groupId>com.giffing.bucket4j.spring.boot.starter</groupId>
<artifactId>bucket4j-spring-boot-starter</artifactId>
<version>0.12.7</version>
</dependency>
<dependency>
<groupId>com.bucket4j</groupId>
<artifactId>bucket4j-redis</artifactId>
<version>8.10.1</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.
接下来的案例是基于redis的,所以引入了jedis。你也可以是lettuce或者是redisson但是这2个貌似需要是webflux环境。
jedis配置
@Bean
public JedisPool jedisPool(
@Value("${spring.data.redis.port}") Integer port,
@Value("${spring.data.redis.host}") String host,
@Value("${spring.data.redis.password}") String password,
@Value("${spring.data.redis.database}") Integer database
) {
// buildPoolConfig方法自己进行配置吧
final JedisPoolConfig poolConfig = buildPoolConfig();
return new JedisPool(poolConfig, host, port, 60000, password, database);
}
1.2.3.4.5.6.7.8.9.10.11.
以上基础环境就准备好了,接下来就可以进行规则配置。而规则的配置可以基于2中方式,基于配置文件和基于注解(AOP)。
定义接口
@RestController
@RequestMapping("/products")
public class ProductController {
@GetMapping("/{id}")
public Product getProduct(@PathVariable Integer id) {
return new Product(id, "商品 - " + id, BigDecimal.valueOf(new Random().nextDouble(1000))) ;
}
}
1.2.3.4.5.6.7.8.9.10.
接下来我将基于上面的接口进行限流的配置。
2.2 基于配置文件
基于配置文件的规则配置底层实现是通过Filter。
bucket4j:
cache-to-use: redis-jedis
filter-config-caching-enabled: true
filters:
- cache-name: product_cache_name
id: product_filter
# 配置请求url的规则;这里底层是通过正则表达式进行匹配的
url: /products/.*
rate-limits:
-
#这里的cache-key非常关键;用于区分不同请求的情况;
#比如,这里我会根据不同的请求id来现在访问速率
#这里可以写spel表达式,这里调用的是HttpServletRequest#getParameter方法
cache-key: getParameter("id")
bandwidths:
#配置桶的容量
- capacity: 2
# 时间
time: 30
# 单位
unit: seconds
# 填充速度;这会每隔30秒进行填充
refill-speed: interval
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.
修改默认的限流提示
bucket4j:
filters:
- cache-name: product_cache_name
http-content-type: 'application/json;charset=utf-8'
http-response-body: '{"code": -1, "message": "请求太快了"}'
1.2.3.4.5.
注意:你必须同时要设置content-type设置字符编码,否则会乱码。
条件放行
你也可以通过如下属性进行有条件的放行;
bucket4j:
filters:
- cache-name: product_cache_name
rate-limits:
-
skip-condition: 'getParameter("id").equals("6")'
1.2.3.4.5.6.
当请求id的值为6时则跳过规则,直接方向。
以上是基于配置文件规则的应用,它还有很多其它的配置属性,详细查看官方文档
github.com/MarcGiffing…[1]
接下来介绍基于注解的方式。
2.3 基于注解
通过"@RateLimiting"注解,AOP 可以拦截目标方法。这样,你就可以全面访问方法参数,轻松定义速率限制键或有条件地跳过速率限制。
配置文件中配置规则
bucket4j:
methods:
- name: storage_rate #在代码中会通过该名称引用
cache-name: storage_cache_name
rate-limit:
bandwidths:
- capacity: 2
time: 30
unit: seconds
refill-speed: interval
1.2.3.4.5.6.7.8.9.10.
接口注解,配置限流
@RateLimiting(
name = "storage_rate",
cacheKey = "'storage-' + #id",
skipCondition = "#name eq 'admin'",
ratePerMethod = true,
fallbackMethodName = "getStorageFallback"
)
@GetMapping("/{id}")
public R<Storage> getStorage(@PathVariable Integer id, String name) {
return R.success(new Storage(id, "SP001 - " + id, new Random().nextInt(10000))) ;
}
// 回退方法的签名必须与业务方法一致
public R<Storage> getStorageFallback(Integer id, String name) {
return R.failure(String.format("请求id=%d,name=%s被限流", id, name)) ;
}
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.
skipCondition:该属性定义了如果请求的name的值为admin则跳过,不限流。
@RateLimiting注解还可以应用到类中,这样该类中的所有方法都会被限流,如下示例:
@Service
@RateLimiting(
name = "storage_rate",
cacheKey = "getName",
ratePerMethod = false
)
public class StorageService {
public Storage queryStorageById(Integer id) {
return new Storage(id, "SP001 - " + id, new Random().nextInt(10000)) ;
}
@IgnoreRateLimiting
public List<Storage> queryStorages() {
return List.of(
new Storage(1, "SP001 - " + 1, new Random().nextInt(10000)),
new Storage(2, "SP002 - " + 2, new Random().nextInt(10000)),
new Storage(3, "SP003 - " + 3, new Random().nextInt(10000))
) ;
}
}
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.
上面代码queryStorageById会被限流,而queryStorages方法被@IgnoreRateLimiting注解标准,所以不会被限流。
原文:https://juejin.cn/post/7409547302748160050
作者:星辰大海的精灵
Reference
[1]https://github.com/MarcGiffing/bucket4j-spring-boot-starter: https://github.com/MarcGiffing/bucket4j-spring-boot-starter
相关推荐
- 为何越来越多的编程语言使用JSON(为什么编程)
-
JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...
- 何时在数据库中使用 JSON(数据库用json格式存储)
-
在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...
- MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)
-
前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...
- JSON对象花样进阶(json格式对象)
-
一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...
- 深入理解 JSON 和 Form-data(json和formdata提交区别)
-
在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...
- JSON 语法(json 语法 priority)
-
JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...
- JSON语法详解(json的语法规则)
-
JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...
- MySQL JSON数据类型操作(mysql的json)
-
概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...
- JSON的数据模式(json数据格式示例)
-
像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...
- 前端学习——JSON格式详解(后端json格式)
-
JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...
- 什么是 JSON:详解 JSON 及其优势(什么叫json)
-
现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...
- PostgreSQL JSON 类型:处理结构化数据
-
PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...
- JavaScript:JSON、三种包装类(javascript 包)
-
JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...
- Python数据分析 只要1分钟 教你玩转JSON 全程干货
-
Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...
- 比较一下JSON与XML两种数据格式?(json和xml哪个好)
-
JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- r语言矩阵 (127)
- browsererror (114)
- exportexcel (119)
- cv2.bitwise_not (137)
- dump命令 (128)
- es6concat (126)
- heapify (127)
- java.security.egd (130)
- javax.annotation (117)
- jsstringsplit (117)
- js数字 (115)
- maven编译 (132)
- mysqlleft (128)
- nodejsbuffer (149)
- org.apache.commons.httpclient (126)
- org.jsoup (141)
- org.springframework.web (128)
- robotframework-ride (115)
- setnocounton (141)
- socket.gethostbyname (122)
- sqlmid (121)
- time.strptime (133)
- vscode格式化 (125)
- win32con (129)
- window.localstorage (126)