9

我正在创建一个大型 CSV 响应的 Perl 模块的一部分。服务器在 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;

但是,该query_data函数不是一个快速的函数,并且会检索很多记录。在那里,我只是将每一行连接起来$body,并且在处理完所有行之后,发送整个响应。

我不喜欢这个有两个明显的原因:首先,它需要大量的 RAM 直到$body被销毁。其次,在该方法完成工作并实际发送响应之前,用户看不到响应活动$res->body($body)

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

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

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

提前感谢您的任何评论!

4

3 回答 3

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.
        },
    );
};

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

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

Plack 是中间件。你是在上面使用 Web 应用程序框架,比如 Mojolicious 或 Dancer2,还是在它下面使用 Apache 或 Starman 服务器?这将影响缓冲的工作方式。

上面的链接显示了 Plack 的作者的一个例子: https ://metacpan.org/source/MIYAGAWA/Plack-1.0037/eg/dot-psgi/echo-stream-sync.psgi

或者,您可以在 Plack 和 Starman 或 Apache 之上使用 Dancer2 轻松完成: https ://metacpan.org/pod/distribution/Dancer2/lib/Dancer2/Manual.pod#Delayed-responses-Async-Streaming

问候,彼得

于 2016-10-27T09:13:35.637 回答