如何解决如何知道 PSGI 非阻塞流编写器何时准备好以兼容 PSGI 的方式接收更多数据?
我正在编写一个 PSGI 中间件,目前正在 Twiggy 服务器上运行。中间件处理大型 (>2GB) 动态创建的文件,并利用 Twiggy/AnyEvent 的异步流传输能力。
PSGI Specification 非常简短地说明了流式响应:
...响应者必须返回另一个实现 write
和 close
方法的对象。 ...
挖掘 Twiggy 代码,它使用 AnyEvent::Handle::push_write
来实现上述 write
方法。如果您继续以比将数据写入网络的速度更快的速度向其提供大量数据,这将耗尽您所有的 RAM。
当然 AnyEvent::Handle
有方法并利用回调来处理缓冲区大小(即 on_drain
事件处理程序指示何时写入缓冲区为空,wbuf_max
以限制写入缓冲区大小)。
然而,使用这些功能将是特定于服务器的,并限制了 PSGI 应用程序的可移植性。 PSGI 规范似乎没有涵盖用于控制/监视异步写入流或访问底层文件句柄/描述符以进行手动检查的 API。
其他人如何以跨 PSGI Web 服务器“兼容”的方式解决内存使用/缓冲或知道异步写入何时完成?任何指针都会很棒。
解决方法
作为跟进,我想我会发布一个我如何解决我的问题的简化版本,以防它对其他人有所帮助。
使用 {handle}
中使用的 AnyEvent::Handle
中的 writer
元素,我手动设置了 on_drain
和 on_error
的回调。
当写缓冲区为空时调用 on_drain
。因此处理程序使我的数据生成代码能够继续生成数据。
当调用数据生成回调时,数据将写入响应并禁用/暂停数据生成。
当 on_drain
处理程序再次启用数据生成时,循环继续。
这可以控制 writer
的内存使用情况,现在使用最少的内存来处理大型流响应。我似乎仍然有一些缓慢的内存泄漏问题,但这可能是我在别处的代码。
sub call {
my ($self,$env)=@_;
#URL/path matching here
my $myASYNCObject; #Complicated async object setup
my $onDrain= sub { #on_drain handler
$myAsyncObject->continue; #tell generation code to continue
};
return sub {
#Boilerplate for streaming response
my $responder=shift;
my $resCode=200;
my $resHeaders=[...];
my $writer=$responder->([$resCode,$resHeaders]);
#Setup callback and start data generation
$myAsyncObject->setCallback=sub{
my $myData=shift;
$writer->write->($myData); #Write the data
$myAsyncObject->pause; #Tell generation code to pause
};
$writer->{handle}->on_drain( #Setup on_drain handler
sub {
$myAsyncObject->continue; #tell generation code to continue
}
);
#Error handlers here...
}
}