在 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 给你:)
- https://metacpan.org/pod/PSGI#Delayed-Response-and-Streaming-Body
- https://metacpan.org/pod/Plack::Middleware::BufferedStreaming
- https://metacpan.org/source/MIYAGAWA/Plack-1.0037/eg/dot-psgi/echo-stream.psgi
- https://metacpan.org/source/MIYAGAWA/Plack-1.0037/eg/dot-psgi/nonblock-hello.psgi
所以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
对象有两个方法,write
和 close
,它们的作用完全正如它们的名字所暗示的那样。不要忘记调用 close
方法来完成响应;上面的代码没有显示这一点,因为它的调用方式取决于 query_data
和您的其他代码的工作方式。
- 大多数服务器都支持这样的流式传输。您可以检查
$env->{'psgi.streaming'}
以确保您的确实如此。
我正在创建大型 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 给你:)
- https://metacpan.org/pod/PSGI#Delayed-Response-and-Streaming-Body
- https://metacpan.org/pod/Plack::Middleware::BufferedStreaming
- https://metacpan.org/source/MIYAGAWA/Plack-1.0037/eg/dot-psgi/echo-stream.psgi
- https://metacpan.org/source/MIYAGAWA/Plack-1.0037/eg/dot-psgi/nonblock-hello.psgi
所以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
对象有两个方法,write
和close
,它们的作用完全正如它们的名字所暗示的那样。不要忘记调用close
方法来完成响应;上面的代码没有显示这一点,因为它的调用方式取决于query_data
和您的其他代码的工作方式。- 大多数服务器都支持这样的流式传输。您可以检查
$env->{'psgi.streaming'}
以确保您的确实如此。