在Plack中发送无缓冲的响应

Fra*_*ozo 9 perl plack uwsgi psgi

我正在使用Perl模块的一部分创建一个大型CSV响应.服务器运行在Plack上,我远非专家.

目前我正在使用这样的东西发送回复:

$res->content_type('text/csv');
my $body = '';
query_data (
    parameters  => \%query_parameters,
    callback    => sub {
        my $row_object = shift;
        $body .= $row_object->to_csv;
    },
);
$res->body($body);
return $res->finalize;
Run Code Online (Sandbox Code Playgroud)

但是,该query_data函数不是一个快速的函数,并检索大量的记录.在那里,我只是将每一行连接进去,$body并且在处理完所有行之后,发送整个响应.

我不喜欢这两个原因:首先,它需要大量的RAM才能$body被破坏.其次,在该方法完成工作并实际发送响应之前,用户看不到任何响应活动$res->body($body).

我试图在文档中找到答案,但没有找到我需要的东西.

我也试过调用$res->body($row_object->to_csv)我的回调部分,但似乎最终只发送了我做的最后一次调用$res->body,覆盖了所有以前的调用.

有没有办法发送Plack响应来刷新每行的内容,因此用户在收集数据时实时开始接收内容,而不必将所有数据累积到可靠的第一个?

提前感谢您的任何意见!

ccm*_*ccm 2

您不能使用Plack::Response,因为该类旨在表示完整的响应,并且您永远不会在内存中同时拥有完整的响应。您尝试做的事情称为流式传输,即使 Plack::Response 不支持, PSGI 也支持它。

以下是您可以如何实现它(改编自您的示例代码):

my $env = shift;

if (!$env->{'psgi.streaming'}) {
    # do something else...
}

# Immediately start the response and stream the content.
return sub {
    my $responder = shift;
    my $writer = $responder->([200, ['Content-Type' => 'text/csv']]);

    query_data(
        parameters  => \%query_parameters,
        callback    => sub {
            my $row_object = shift;
            $writer->write($row_object->to_csv);
            # TODO: Need to call $writer->close() when there is no more data.
        },
    );
};
Run Code Online (Sandbox Code Playgroud)

关于这段代码的一些有趣的事情:

  • Plack::Response您可以返回一个 ,而不是返回一个对象sub。该子例程将在一段时间后被调用以获得实际响应。PSGI 支持这一点以允许所谓的“延迟”响应。
  • 我们返回的子例程获取一个参数,该参数是应该调用并传递真实响应的a coderef(在本例中为)。$responder如果真实响应不包含“主体”(即通常是 的第三个元素)arrayref,则将$responder返回一个我们可以将主体写入其中的对象。PSGI 支持此功能以允许流式响应。
  • $writer对象有两个方法,write它们close的作用正如它们的名字所暗示的那样。不要忘记调用该close方法来完成响应;上面的代码没有显示这一点,因为它的调用方式取决于query_data您的其他代码的工作方式。
  • 大多数服务器都支持这样的流式传输。您可以检查一下$env->{'psgi.streaming'}以确保您的确实如此。