12个版本 (4个重大更新)
0.4.2 | 2024年4月20日 |
---|---|
0.3.2 | 2024年2月3日 |
0.3.1 | 2024年1月18日 |
0.2.0 | 2023年12月5日 |
0.0.1 | 2022年10月18日 |
#17 in HTTP服务器
每月下载 29,614次
用于 3 个crate
65KB
1K SLoC
一个Tower服务和层,通过governor提供速率限制后端。在很大程度上基于actix-governor的工作。与Axum、Hyper、Tonic以及其他基于Tower的任何东西兼容!
特性
- 基于对等方IP地址、IP地址头、全局或自定义键来限制请求速率
- 每秒或特定爆发自定义流量限制标准
- 简单易用
- 高度可定制
- 高性能
- 强大而灵活的API
它是如何工作的?
每个governor中间件都有一个配置,存储配额。配额指定在中间件开始阻止进一步请求之前,可以从IP地址发送多少个请求。
例如,如果配额允许十个请求,则客户端可以在中间件开始阻止之前短时间内发送十个请求的爆发。
一旦至少使用了配额的一个元素,配额的元素将在指定的时间后得到补充。
例如,如果这个周期是2秒,配额为空,则补充一个配额元素需要2秒。这意味着你平均每两秒可以发送一个请求。
如果有允许十个请求的配额,并且周期相同,则客户端可以再次发送十个请求的爆发,然后必须等待两秒才能发送进一步的请求,或者等待20秒,直到完全补充配额后才能发送另一个爆发。
示例
use axum::{error_handling::HandleErrorLayer, routing::get, BoxError, Router};
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use tokio::net::TcpListener;
use tower::ServiceBuilder;
use tower_governor::{governor::GovernorConfigBuilder, GovernorLayer};
async fn hello() -> &'static str {
"Hello world"
}
#[tokio::main]
async fn main() {
// Configure tracing if desired
// construct a subscriber that prints formatted traces to stdout
let subscriber = tracing_subscriber::FmtSubscriber::new();
// use that subscriber to process traces emitted after this point
tracing::subscriber::set_global_default(subscriber).unwrap();
// Allow bursts with up to five requests per IP address
// and replenishes one element every two seconds
// We Box it because Axum 0.6 requires all Layers to be Clone
// and thus we need a static reference to it
let governor_conf = Arc::new(
GovernorConfigBuilder::default()
.per_second(2)
.burst_size(5)
.finish()
.unwrap(),
);
let governor_limiter = governor_conf.limiter().clone();
let interval = Duration::from_secs(60);
// a separate background task to clean up
std::thread::spawn(move || {
loop {
std::thread::sleep(interval);
tracing::info!("rate limiting storage size: {}", governor_limiter.len());
governor_limiter.retain_recent();
}
});
// build our application with a route
let app = Router::new()
// `GET /` goes to `root`
.route("/", get(hello))
.layer(GovernorLayer {
config: governor_conf,
});
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::debug!("listening on {}", addr);
let listener = TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app.into_make_service_with_connect_info::<SocketAddr>())
.await
.unwrap();
}
配置预设
您可以使用预定义的预设而不是使用配置构建器。
-
GovernorConfig::default()
:适用于大多数服务的默认配置。允许最多八个请求的爆发,并在基于对等方IP的情况下,在500ms后补充一个元素。 -
GovernorConfig::secure()
:安全相关服务的默认配置。基于对等方IP,允许最多两个请求的突发,并在四秒后补充一个元素。
例如,可以将安全配置用作此代码的简短版本
use tower_governor::governor::GovernorConfigBuilder;
let config = GovernorConfigBuilder::default()
.per_second(4)
.burst_size(2)
.finish()
.unwrap();
自定义速率限制键
默认情况下,速率限制使用对等方IP地址(即请求您的应用程序的HTTP客户端的IP地址:您的用户或反向代理,具体取决于您的部署设置)。您可以配置不同的行为,
- 这本身可能很有用
- 允许您根据不同的键设置此中间件的多实例(例如,如果您想在同一时间应用不同速率的IP和API键的速率限制)
这是通过定义一个[KeyExtractor]并将其提供给一个[Governor]实例来实现的。提供了三个可用的键提取器
- [PeerIpKeyExtractor]:这是默认的,它使用请求的对等方IP地址。
- [SmartIpKeyExtractor]:查找通常由反向代理提供的常见IP标识头(按顺序:x-forwarded-for,x-real-ip,forwarded),如果找不到,则回退到对等方IP地址。
- [GlobalKeyExtractor]:对所有传入请求使用相同的键
有关更多信息,请参阅custom_key_bearer示例。
存储功能标志
tower-governor使用功能标志来减少编译的代码量,并且可以启用某些功能而禁用其他功能。以下是可用功能标志的列表
axum
:启用对axum Web框架的支持tracing
:启用此中间件的跟踪输出
无默认功能的示例
- 禁用
default
功能将改变[PeerIpKeyExtractor]和[SmartIpKeyExtractor]的行为:这两个键提取器将期望从's Extensions中获取SocketAddr类型。 - 未能提供有效的SocketAddr可能导致GovernorError::UnableToExtractKey。
Cargo.toml
[dependencies]
tower-governor = { version = "0.3", default-features = false }
main.rs
use std::{convert::Infallible, net::SocketAddr};
use std::sync::Arc;
use http::{Request, Response};
use tower::{service_fn, ServiceBuilder, ServiceExt};
use tower_governor::{governor::GovernorConfigBuilder, GovernorLayer};
# async fn service() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// service function expecting rate limiting by governor.
let service = service_fn(|_: Request<()>| async {
Ok::<_, Infallible>(Response::new(String::from("mock response")))
});
let config = Arc::new(GovernorConfigBuilder::default().finish().unwrap());
// build service with governor layer
let service = ServiceBuilder::new()
// the caller of service must provide SocketAddr to governor layer middleware
.map_request(|(mut req, addr): (Request<()>, SocketAddr)| {
// insert SocketAddr to request's extensions and governor is expecting it.
req.extensions_mut().insert(addr);
req
})
.layer(GovernorLayer { config })
.service(service);
// mock client socket addr and http request.
let addr = "127.0.0.1:12345".parse().unwrap();
let req = Request::default();
// execute service
service.oneshot((req, addr)).await?;
# Ok(())
# }
添加x-ratelimit头
默认情况下,启用了x-ratelimit-after
,但如果您想启用x-ratelimit-limit,
x-ratelimit-whitelisted
和x-ratelimit-remaining
,请在您的GovernorConfig上使用.use_headers()
方法。
错误处理
此存储库会显示带有建议头的GovernorError,并包括GovernorConfigBuilder::error_handler
方法,该方法将那些错误转换为Response。您可以为接受GovernorError
并返回Response的自定义错误处理器。
常见陷阱
-
不要多次构建相同的配置,除非明确需要!这将为每个配置创建一个独立的速率限制器!相反,像示例中描述的那样,将相同的配置引用传递给
Governor::new()
。 -
如果你使用的是默认的PeerIpKeyExtractor,请务必使用
.into_make_service_with_connect_info::<SocketAddr>
来创建你的服务器,而不是使用.into_make_service()
。否则,Tower将找不到对等IP地址!
依赖关系
~8–16MB
~203K SLoC