如何使用Tokio实现令牌消耗式速率限制?
现成解决方案推荐
针对你需要的一次性消耗式令牌场景,不需要自己实现原子变量+重试,Rust生态里有几种现成方案:
1. 异步通道(async-channel)
用异步通道模拟令牌池,预先向通道发送若干令牌,每次recv().await获取并消耗令牌,通道空时自动阻塞等待新令牌:
use async_channel::{unbounded, Receiver}; #[tokio::main] async fn main() { let (sender, receiver) = unbounded::<()>(); // 预先放入10个令牌 for _ in 0..10 { sender.send(()).await.unwrap(); } loop { // 获取令牌,无令牌时阻塞 receiver.recv().await.unwrap(); // 执行业务逻辑 println!("处理任务"); } }
需要动态添加令牌时,调用sender.send(()).await即可。
2. 改造tokio::sync::Semaphore
虽然信号量默认是可复用的,但可以通过不归还许可实现消耗式逻辑:
use tokio::sync::Semaphore; #[tokio::main] async fn main() { let semaphore = Semaphore::new(10); loop { // 获取许可后不主动释放(不丢弃`_permit`) let _permit = semaphore.acquire().await.unwrap(); // 执行业务逻辑 println!("处理任务"); } }
许可会随着获取被永久消耗,可通过semaphore.add_permits(n)动态补充。
3. 专用令牌池库ticketed
这个 crate 专为一次性令牌池设计,API 更贴合你的需求:
use ticketed::TicketPool; #[tokio::main] async fn main() { let pool = TicketPool::new(10); loop { let _ticket = pool.acquire().await; // 执行业务逻辑 println!("处理任务"); } }
支持pool.add_tickets(n)动态添加令牌,获取操作会自动阻塞直到有可用令牌。
内容的提问来源于stack exchange,提问作者eof
相关产品推荐
相关产品推荐

