我正在使用futures-rs powered version of the Rusoto AWS Kinesis library。我需要生成AWS Kinesis请求的深层管道以实现高吞吐量,因为Kinesis每个HTTP请求的限制为500条记录。结合发送请求的50ms延迟,我需要开始生成许多并发请求。我希望创建大约100个飞行请求的地方。
Rusoto put_records
函数签名如下所示:
fn put_records(
&self,
input: &PutRecordsInput,
) -> RusotoFuture<PutRecordsOutput, PutRecordsError>
RusotoFuture
是这样定义的包装器:
/// Future that is returned from all rusoto service APIs.
pub struct RusotoFuture<T, E> {
inner: Box<Future<Item = T, Error = E> + 'static>,
}
内部Future
已被包裹但RusutoFuture
仍在实施Future::poll()
,因此我认为它与futures-rs
生态系统兼容。 RusotoFuture
提供同步调用:
impl<T, E> RusotoFuture<T, E> {
/// Blocks the current thread until the future has resolved.
///
/// This is meant to provide a simple way for non-async consumers
/// to work with rusoto.
pub fn sync(self) -> Result<T, E> {
self.wait()
}
}
我可以发出请求并sync()
,从AWS获取结果。我想创建许多请求,将它们放入某种队列/列表中,并收集完成的请求。如果请求出错,我需要重新发出请求(这在Kinesis中有点正常,特别是当你的分片吞吐量达到限制时)。如果请求成功完成,我应该发出一个包含新数据的请求。我可以为每个请求生成一个线程并同步它,但是当我运行异步IO线程时这似乎效率低下。
我尝试从我的应用程序线程中使用futures::sync::mpsc::channel
(不是从Tokio反应器内部运行),但每当我克隆tx
时,它都会生成自己的缓冲区,从而消除{{1}上的任何背压}}:
send
没有克隆,我有错误:
fn kinesis_pipeline(client: DefaultKinesisClient, stream_name: String, num_puts: usize, puts_size: usize) {
use futures::sync::mpsc::{ channel, spawn };
use futures::{ Sink, Future, Stream };
use futures::stream::Sender;
use rusoto_core::reactor::DEFAULT_REACTOR;
let client = Arc::new(KinesisClient::simple(Region::UsWest2));
let data = FauxData::new(); // a data generator for testing
let (mut tx, mut rx) = channel(1);
for rec in data {
tx.clone().send(rec);
}
}
我还根据建议查看了futures::mpsc::sync::spawn
,但它是error[E0382]: use of moved value: `tx`
--> src/main.rs:150:9
|
150 | tx.send(rec);
| ^^ value moved here in previous iteration of loop
|
= note: move occurs because `tx` has type `futures::sync::mpsc::Sender<rusoto_kinesis::PutRecordsRequestEntry>`, which does not implement the `Copy` trait
(作为rx
)的所有者,并没有解决Stream
的问题导致无限制行为的Copy
。
我希望我能使tx
/ channel
用法正常工作,我会有一个系统需要spawn
s,等待它们完成,然后提供给我从我的应用程序线程中获取完成结果的简单方法。
答案 0 :(得分:0)
就channel
而言,我可以告诉您问题不是Sender
的单个克隆将容量增加1,而是为每个项目克隆Sender
你试图发送。
您在没有clone
的情况下看到的错误来自您对Sink::send
界面的错误使用。使用clone
,您实际应该看到警告:
warning: unused `futures::sink::Send` which must be used: futures do nothing unless polled
那就是:您当前的代码实际上并没有发送任何内容!
为了应用背压,您需要将send
次呼叫链接起来;每个人都应该等到上一个完成(你需要等待最后一个!);成功之后,您将获得Sender
。最好的方法是使用iter_ok
从迭代器生成Stream
并将其传递给send_all
。
现在你有一个未来SendAll
,你需要&#34;驾驶&#34;。如果忽略结果并且出错(.then(|r| { r.unwrap(); Ok::<(), ()>(()) })
),您可以将其作为单独的任务生成,但是您可能希望将其集成到主应用程序中(即将其返回到Box
)。 / p>
// this returns a `Box<Future<Item = (), Error = ()>>`. you may
// want to use a different error type
Box::new(tx.send_all(iter_ok(data)).map(|_| ()).map_err(|_| ()))
RusotoFuture::sync
和Future::wait
不要使用Future::wait
:它已经在分支机构中弃用,它通常不会做你真正想要的事情。我怀疑RusotoFuture
是否意识到这些问题,所以我建议避免使用RusotoFuture::sync
。
Sender
会增加频道容量正如您所说,克隆Sender
会将容量增加一个。
这似乎是为了提高性能:Sender
以未阻止(&#34; unparked&#34;)状态开始;如果Sender
未被阻止,则可以不阻止地发送项目。但是,如果Sender
发送项目时队列中的项目数达到配置的限制,Sender
将被阻止(&#34;停放&#34;)。 (从队列中删除项目将在特定时间取消阻止Sender
。)
这意味着在内部队列达到限制后,每个Sender
仍然可以发送一个项目,这会导致记录增加容量的效果,但前提是实际上所有Sender
都在发送项目 - 未使用的Sender
不会增加观察到的容量。
性能的提升来自这样一个事实:只要你没有达到极限,它就不需要停放和通知任务(这是非常沉重的)。
mpsc
模块顶部的私人文档描述了更多详细信息。