Asynchronous 控制衍生期货的数量以产生背压

Asynchronous 控制衍生期货的数量以产生背压,asynchronous,rust,future,amazon-kinesis,Asynchronous,Rust,Future,Amazon Kinesis,我用的是一个。我需要生成AWS Kinesis请求的深层管道以实现高吞吐量,因为Kinesis对每个HTTP请求的记录限制为500条。再加上发送请求的50毫秒延迟,我需要开始生成许多并发请求。我正在寻找创造某处的订单100飞行中的请求 Rusotoput\u记录函数签名如下所示: fn put_records( &self, input: &PutRecordsInput, ) -> RusotoFuture<PutRecordsOutput, Put

我用的是一个。我需要生成AWS Kinesis请求的深层管道以实现高吞吐量,因为Kinesis对每个HTTP请求的记录限制为500条。再加上发送请求的50毫秒延迟,我需要开始生成许多并发请求。我正在寻找创造某处的订单100飞行中的请求

Rusoto
put\u记录
函数签名如下所示:

fn put_records(
    &self,
    input: &PutRecordsInput,
) -> RusotoFuture<PutRecordsOutput, PutRecordsError>
/// Future that is returned from all rusoto service APIs.
pub struct RusotoFuture<T, E> {
    inner: Box<Future<Item = T, Error = E> + 'static>,
}
内部的
Future
已包装,但
rusutofurture
仍实现
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()
    }
}
如果没有克隆,我会出现以下错误:

错误[E0382]:使用移动值:`tx`
-->src/main.rs:150:9
|
150 |发送(rec);
|^^值在循环的上一次迭代中移到此处
|
=注意:发生移动是因为'tx'的类型为'futures::sync::mpsc::Sender',而该类型未实现'Copy'特性
我也根据建议进行了研究,但它拥有
rx
(作为
)的所有权,并且没有解决
tx
副本
导致无限行为的问题


我希望如果我能让
频道
/
产卵
使用正常,我将拥有一个系统,它可以接收
RusotoFuture
s,等待它们完成,然后为我提供了一种从我的应用程序线程获取完成结果的简单方法。

据我所知,您的问题不在于单个克隆会使容量增加一个,而是您为尝试发送的每个项目克隆了
发件人

您在没有克隆的情况下看到的错误是由于不正确使用了
Sink::send
接口。使用
clone
时,您实际上应该会看到警告:

警告:未使用的`futures::sink::Send`必须使用:除非轮询,否则futures不会执行任何操作
也就是说:您当前的代码实际上从未发送过任何东西

为了施加背压,你需要将那些
发送
呼叫链接起来;每一个都应该等到上一个完成(你也需要等到最后一个!);成功后,您将获得
发件人
。最好的方法是使用并将其传递给迭代器,从迭代器生成

现在您有一个未来的
SendAll
,需要“驱动”。如果忽略结果并出现错误(
),那么(|r |{r.unwrap();Ok::(())}
)可以将其作为单独的任务生成,但可能需要将其集成到主应用程序中(即,在
框中返回它)

//这将返回一个“Box”。你可以
//要使用不同的错误类型吗
框::新建(tx.send_all(iter_ok(data)).map(| |()).map_err(| |())
RusotoFuture::sync
Future::wait
不要使用:它在分支中已经被弃用了,并且通常不会满足您的实际需求。我怀疑
RusotoFuture
是否意识到这些问题,因此我建议避免
RusotoFuture::sync

克隆
发送方
增加信道容量 正如您正确指出的那样,克隆
发送方
会将容量增加1

这样做似乎是为了提高性能:
发送方
在未阻塞(“未屏蔽”)状态下启动;如果未阻止
发件人
,则该发件人可以在不阻止的情况下发送项目。但是,如果当
发送方
发送项目时,队列中的项目数量达到配置的限制,则
发送方
将被阻止(“停止”)。(从队列中删除项目将在特定时间解除对
发件人的阻止。)

这意味着,在内部队列达到限制后,每个
发送者
仍然可以发送一个项目,这导致记录的容量增加的效果,但前提是实际上所有
发送者
都在发送项目-未使用的
发送者
不会增加观察到的容量

性能的提升来自这样一个事实:只要你没有达到极限,它就不需要停驻和通知任务(这相当繁重)


模块顶部的私人文档介绍了更多详细信息。

您看过了吗?将它与一个(可能是
不同步的
)频道结合起来,也许它可以满足您的需要。您可能需要通过(
Rc
Arc
)将句柄共享给
mpsc::Sender
。@Stefan为什么需要
Rc
/
Arc
?难道你不能克隆
发送者吗?@Shepmaster是的,它本身已经做了,
克隆
应该没问题。我猜“每当我克隆tx时,tx都会生成它自己的缓冲区”让我感到困惑(这不应该是真的,缓冲区应该被共享)。另外,考虑到你正在构建循环依赖关系,通道(1)
是一个非常小的缓冲区:如果一个请求可以触发一个新的请求,但必须等待推送一个新的请求,直到它完成,它将永远阻塞。我会使用
unbounded()
。请求触发新请求导致死锁这一点很好。目前,我只是想了解请求背压管道的核心思想。我会注意到这是一个未来的问题。
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);
    }
}