#rate-limiting #tower #ip-address #tower-middleware #axum #tonic #governor

tower_governor

一个基于governor包的速率限制中间件,支持可配置的基于键和全局限制

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服务器

Download history 7578/week @ 2024-05-02 8200/week @ 2024-05-09 7644/week @ 2024-05-16 7338/week @ 2024-05-23 6858/week @ 2024-05-30 6796/week @ 2024-06-06 6381/week @ 2024-06-13 7264/week @ 2024-06-20 9740/week @ 2024-06-27 7674/week @ 2024-07-04 6761/week @ 2024-07-11 5854/week @ 2024-07-18 6917/week @ 2024-07-25 7818/week @ 2024-08-01 7534/week @ 2024-08-08 6030/week @ 2024-08-15

每月下载 29,614次
用于 3 个crate

MIT/Apache

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地址:您的用户或反向代理,具体取决于您的部署设置)。您可以配置不同的行为,

  1. 这本身可能很有用
  2. 允许您根据不同的键设置此中间件的多实例(例如,如果您想在同一时间应用不同速率的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-limitx-ratelimit-whitelistedx-ratelimit-remaining,请在您的GovernorConfig上使用.use_headers()方法。

错误处理

此存储库会显示带有建议头的GovernorError,并包括GovernorConfigBuilder::error_handler方法,该方法将那些错误转换为Response。您可以为接受GovernorError并返回Response的自定义错误处理器。

常见陷阱

  1. 不要多次构建相同的配置,除非明确需要!这将为每个配置创建一个独立的速率限制器!相反,像示例中描述的那样,将相同的配置引用传递给Governor::new()

  2. 如果你使用的是默认的PeerIpKeyExtractor,请务必使用.into_make_service_with_connect_info::<SocketAddr>来创建你的服务器,而不是使用.into_make_service()。否则,Tower将找不到对等IP地址!

依赖关系

~8–16MB
~203K SLoC