12 个不稳定版本 (5 个重大变更)
0.6.0 | 2024 年 5 月 3 日 |
---|---|
0.6.0-rc.1 | 2024 年 4 月 22 日 |
0.5.0 | 2023 年 9 月 13 日 |
0.4.0 | 2023 年 5 月 3 日 |
0.1.1 | 2020 年 7 月 29 日 |
#273 in 网页编程
每月 750 次下载
205KB
5K SLoC
Limitador (库)
一个可嵌入的速率限制器库,支持内存、Redis 和 Infinispan 数据存储。Limitador 还可以编译成 WebAssembly。
关于 crate API 的完整文档,请参阅 docs.rs
特性
redis_storage
:支持使用 Redis 作为数据存储后端。infinispan_storage
:支持使用 Infinispan 作为数据存储后端。lenient_conditions
:支持已弃用的Condition
语法。default
:默认为redis_storage
。
WebAssembly 支持
要在编译为 WASM 的项目中使用 Limitador,需要禁用一些功能。将以下内容添加到您的 Cargo.toml
中
[dependencies]
limitador = { version = "0.3.0", default-features = false }
lib.rs
:
Limitador 是一个通用的速率限制器。
基本操作
Limitador 可以在内存或 Redis 中存储计数器。在内存中存储更快,但计数器不能在多个 Limitador 实例之间共享。在 Redis 中存储较慢,但可以在实例之间共享。
默认情况下,速率限制器配置为在内存中存储计数器。它将仅存储有限数量的“合格计数器”,该计数器以构造函数中的 u64
值指定。
use limitador::RateLimiter;
let rate_limiter = RateLimiter::new(1000);
要使用 Redis
#[cfg(feature = "redis_storage")]
use limitador::RateLimiter;
use limitador::storage::redis::RedisStorage;
// Default redis URL (redis://127.0.0.1:6379).
let rate_limiter = RateLimiter::new_with_storage(Box::new(RedisStorage::default()));
// Custom redis URL
let rate_limiter = RateLimiter::new_with_storage(
Box::new(RedisStorage::new("redis://127.0.0.1:7777").unwrap())
);
限制
限制的定义包括
- 一个命名空间,用于标识要限制的资源。它可以是 API、Kubernetes 服务、代理 ID 等。
- 一个值。
- 周期长度的秒数。
- 定义何时应用限制的条件。
- 一组变量。例如,如果我们需要为每个“user_id”定义相同的限制,而不是为每个硬编码的 ID 创建限制,我们只需将“user_id”定义为变量。
如果我们使用 Limitador 的上下文接收 HTTP 请求,我们可以定义如下限制以允许在 HTTP 方法为“GET”时每个用户 ID 每分钟 10 个请求。
use limitador::limit::Limit;
let limit = Limit::new(
"my_namespace",
10,
60,
vec!["req.method == 'GET'"],
vec!["user_id"],
);
请注意,键和变量是通用的,因此它们不一定必须指向一个HTTP请求。
管理限制
use limitador::RateLimiter;
use limitador::limit::Limit;
let limit = Limit::new(
"my_namespace",
10,
60,
vec!["req.method == 'GET'"],
vec!["user_id"],
);
let mut rate_limiter = RateLimiter::new(1000);
// Add a limit
rate_limiter.add_limit(limit.clone());
// Delete the limit
rate_limiter.delete_limit(&limit);
// Get all the limits in a namespace
let namespace = "my_namespace".into();
rate_limiter.get_limits(&namespace);
// Delete all the limits in a namespace
rate_limiter.delete_limits(&namespace);
应用限制
use limitador::RateLimiter;
use limitador::limit::Limit;
use std::collections::HashMap;
let mut rate_limiter = RateLimiter::new(1000);
let limit = Limit::new(
"my_namespace",
2,
60,
vec!["req.method == 'GET'"],
vec!["user_id"],
);
rate_limiter.add_limit(limit);
// We've defined a limit of 2. So we can report 2 times before being
// rate-limited
let mut values_to_report: HashMap<String, String> = HashMap::new();
values_to_report.insert("req.method".to_string(), "GET".to_string());
values_to_report.insert("user_id".to_string(), "1".to_string());
// Check if we can report
let namespace = "my_namespace".into();
assert!(!rate_limiter.is_rate_limited(&namespace, &values_to_report, 1).unwrap());
// Report
rate_limiter.update_counters(&namespace, &values_to_report, 1).unwrap();
// Check and report again
assert!(!rate_limiter.is_rate_limited(&namespace, &values_to_report, 1).unwrap());
rate_limiter.update_counters(&namespace, &values_to_report, 1).unwrap();
// We've already reported 2, so reporting another one should not be allowed
assert!(rate_limiter.is_rate_limited(&namespace, &values_to_report, 1).unwrap());
// You can also check and report if not limited in a single call. It's useful
// for example, when calling Limitador from a proxy. Instead of doing 2
// separate calls, we can issue just one:
rate_limiter.check_rate_limited_and_update(&namespace, &values_to_report, 1, false).unwrap();
异步
有两种Redis驱动程序,一个是阻塞的,另一个是异步的。要使用异步驱动程序,我们需要使用“AsyncRedisStorage”实例化一个“AsyncRateLimiter”。
#[cfg(feature = "redis_storage")]
use limitador::AsyncRateLimiter;
use limitador::storage::redis::AsyncRedisStorage;
async {
let rate_limiter = AsyncRateLimiter::new_with_storage(
Box::new(AsyncRedisStorage::new("redis://127.0.0.1:7777").await.unwrap())
);
};
阻塞和异步限制器公开了相同的函数,因此我们可以像上面解释的那样使用异步限制器。例如
#[cfg(feature = "redis_storage")]
use limitador::AsyncRateLimiter;
use limitador::limit::Limit;
use limitador::storage::redis::AsyncRedisStorage;
let limit = Limit::new(
"my_namespace",
10,
60,
vec!["req.method == 'GET'"],
vec!["user_id"],
);
async {
let rate_limiter = AsyncRateLimiter::new_with_storage(
Box::new(AsyncRedisStorage::new("redis://127.0.0.1:7777").await.unwrap())
);
rate_limiter.add_limit(limit);
};
限制精度
当将计数器存储在内存中时,Limitador保证我们永远不会超过定义的限制。然而,当使用Redis时并非如此。Redis驱动程序在应用限制时牺牲了一点精度,以提高性能。
依赖项
~7–26MB
~411K SLoC