Rust 异步流实战:用 futures::stream 做并发拉取与背压
test12026-10-2915 次阅读
为什么用 Stream
一次拉一个 future 用 await 足够;要处理“一串异步产生的值”(分页抓取、消息订阅、文件分块)时,Stream 比手动维护 Vec
一、把一个迭代器变成流
用 stream::iter 把普通集合包成 Stream,再逐个异步处理。
use futures::stream::{self, StreamExt};
let urls = vec!["/a", "/b", "/c"];
let mut s = stream::iter(urls).map(|u| async move {
format!("fetched {u}")
});
while let Some(v) = s.next().await {
println!("{v}");
}二、并发拉取并限制并行度
for_each_concurrent 配合 buffer_unordered 限制同时最多 4 个在途请求,天然形成背压,不会因为目标很多就瞬间打满连接。
use futures::stream::{self, StreamExt};
let tasks = 0..100;
stream::iter(tasks)
.map(|i| async move {
reqwest::get(format!("https://example.com/{i}")).await
})
.buffer_unordered(4) // 最多 4 个在途
.for_each_concurrent(4, |resp| async move {
if let Ok(r) = resp {
println!("status {}", r.status());
}
})
.await;三、用 fold 做流式聚合
不想 collect 到内存,可以用 fold 边流边累计,处理大量数据时内存占用稳定。
use futures::stream::{self, StreamExt};
let total: usize = stream::iter(1..=1000)
.map(|n| async move { n })
.fold(0usize, |acc, n| async move { acc + n })
.await;四、自己实现异步流
需要自定义产出节奏时,实现 Stream trait 的 poll_next,决定什么时候给下一个值、什么时候 Pending。
use std::pin::Pin;
use std::task::{Context, Poll};
use futures::stream::Stream;
struct Counter { n: u32, max: u32 }
impl Stream for Counter {
type Item = u32;
fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<u32>> {
if self.n >= self.max {
Poll::Ready(None)
} else {
let v = self.n;
self.n += 1;
Poll::Ready(Some(v))
}
}
}小结
Stream 把“一串异步值”的表达标准化:buffer_unordered 控并发、fold 做流式聚合、自己实现 poll_next 定节奏。比起手动管理一堆 JoinHandle,背压和取消都能借由流的撤销一次性处理干净。
T
test1
文章作者
为什么用 Stream 一次拉一个 future 用 await 足够;要处理“一串异步产生的值”(分页抓取、消息订阅、...
- 分类
- 技术
- 发布时间
- 2026-10-29
- 字数
- 约 1789 字
- 阅读
- 15 次
