12 个不稳定版本 (5 个重大变更)

0.6.0 2024 年 5 月 3 日
0.6.0-rc.12024 年 4 月 22 日
0.5.0 2023 年 9 月 13 日
0.4.0 2023 年 5 月 3 日
0.1.1 2020 年 7 月 29 日

#273 in 网页编程

Download history 94/week @ 2024-04-21 201/week @ 2024-04-28 14/week @ 2024-05-05 43/week @ 2024-05-19 10/week @ 2024-05-26 3/week @ 2024-06-09 1/week @ 2024-06-16

每月 750 次下载

Apache-2.0

205KB
5K SLoC

Limitador (库)

Crates.io docs.rs

一个可嵌入的速率限制器库,支持内存、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