【问题标题】:Sending an unbuffered response in Plack在 Plack 中发送无缓冲响应
【发布时间】:2015-07-28 00:24:58
【问题描述】:

我正在创建一个大型 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 被销毁之前需要大量内存。其次,在该方法完成工作并实际使用$res->body($body) 发送响应之前,用户看不到响应活动。

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

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

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

提前感谢任何 cmets!

【问题讨论】:

  • 我没有尝试过,但您应该可以使用带有getline 方法的对象。有关更多详细信息,请发布一个简短但完整的示例。
  • 谢谢思南。是的,您提到的是正确的,我尝试了一个实现getline 的简单对象,它工作正常,除了Plack 仍在缓冲响应并且在->getline 未定义之前不会向浏览器发送任何内容。关于我的示例,我对其进行了修改以使其更加不言自明。发布我的真实代码的唯一真正区别是添加了许多不相关的行。我唯一想弄清楚的是如何让 Plack 发送无缓冲/自动刷新的响应。
  • 您的代码中没有 get_data 函数。我想你的意思是query_data
  • 发布一个简短但完整的示例可以让您减少我为尝试可能的解决方案所做的工作。

标签: perl uwsgi plack psgi


【解决方案1】:

您不能使用Plack::Response,因为该类旨在表示完整的响应,并且您永远不会一次在内存中获得完整的响应。您尝试做的事情称为流式传输,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.
        },
    );
};

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

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

【讨论】:

  • 太棒了!谢谢你。 :-)
【解决方案2】:

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

问候,彼得

【讨论】:

    【解决方案3】:
    猜你喜欢
    • 2013-09-22
    • 1970-01-01
    • 2021-07-14
    • 1970-01-01
    • 2020-02-24
    • 2018-09-20
    • 2019-12-03
    • 1970-01-01
    • 2022-06-23
    相关资源
    最近更新 更多