在Plack中发送无缓存响应

9
我正在处理一个 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;

然而,query_data 函数并不快速且检索了大量的记录。在这里,我只是将每一行连接到 $body 中,在所有行被处理后发送整个响应。
我不喜欢这个方法有两个明显的原因:首先,直到销毁 $body 之前它会占用大量的 RAM。其次,在该方法完成工作并使用 $res->body($body) 实际发送响应之前,用户看不到任何响应活动。
我尝试在文档中查找 答案,但没有找到所需内容。
我还尝试在回调部分调用 $res->body($row_object->to_csv),但似乎最终只发送我对 $res->body 的最后一次调用,覆盖了所有先前的调用。
是否有一种方式可以发送 Plack 响应并在每一行上刷新内容,以便用户在收集数据时实时开始接收内容,而无需先累积所有数据到一个变量中?
非常感谢您的任何评论!

我没有尝试过这个,但是你应该可以使用一个带有 getline 方法的对象。如果需要更多细节,请发布一个简短但完整的示例。 - Sinan Ünür
谢谢Sinan。是的,你提到的是正确的,我尝试了一个实现getline的简单对象,它工作得很好,除了Plack仍然缓冲响应并且在未定义->getline之前不会向浏览器发送任何内容。关于我的示例,我修改了它使其更加自说明。发布我的真实代码唯一的真正区别是添加了许多不相关的行。我唯一想弄清楚的事情就是如何使Plack发送未缓冲/自动刷新的响应。 - Francisco Zarabozo
你的代码中没有 get_data 函数。我想你的意思是 query_data - simbabque
2
发布一个简短但完整的示例可以减少我尝试可能解决方案所需的工作量。 - Sinan Ünür
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对象,而是可以返回一个sub。这个子例程稍后会被调用以获取实际响应。PSGI支持这种方式,以便实现所谓的“延迟”响应。
  • 我们返回的子例程会得到一个参数,它是一个coderef(在这种情况下是$responder),应该调用并传递真正的响应。如果真正的响应中没有包含“body”(即通常是arrayref的第3个元素),则$responder将返回一个对象,我们可以写入body。PSGI支持这种方式,以便实现流式响应。
  • $writer对象有两个方法:writeclose,它们分别执行它们名称所示的操作。不要忘记调用close方法以完成响应;上面的代码没有显示这一点,因为如何调用它取决于query_data和其他代码的工作方式。
  • 大多数服务器都支持这种流式响应。您可以检查$env->{'psgi.streaming'}来确保您的服务器支持此功能。

太棒了!谢谢你。 :-) - Francisco Zarabozo

-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

敬礼,彼得



网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接