在 Plack 中发送无缓冲响应

Sending an unbuffered response in Plack

我正在创建大型 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,并在处理完所有行后发送整个响应。

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

我试图找到这个 in the documentation 的答案,但没有找到我需要的东西。

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

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

提前感谢您的任何评论!

一些阅读 material 给你:)

所以copy/paste/adapt请回来报告

Plack 是中间件。您是否在其之上使用 Web 应用程序框架,例如 Mojolicious 或 Dancer2,或者在其下方使用 Apache 或 Starman 服务器之类的东西?这会影响缓冲的工作方式。

上面的 link 显示了 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

此致,彼得

你不能使用 Plack::Response because that class is intended for representing a complete response, and you'll never have a complete response in memory at one time. What you're trying to do is called streaming, and PSGI supports it 即使 Plack::Response 没有。

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

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

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

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