首页 > 解决方案 > 如何知道 PSGI 非阻塞流式编写器何时以 PSGI 兼容的方式准备好接收更多数据?

问题描述

我正在编写一个 PSGI 中间件,目前在 Twiggy 服务器上运行。中间件处理大型 (>2GB) 动态创建的文件并利用 Twiggy/AnyEvent 的异步流传输能力。

PSGI规范非常简要地说明了流式响应:

...响应者必须返回另一个实现writeclose方法的对象。...

挖掘 Twiggy 代码,它用于AnyEvent::Handle::push_write实现上述write方法。如果您继续向其提供大量数据的速度比将其写入网络的速度要快,这将耗尽您的所有 RAM。

当然AnyEvent::Handle有方法并利用回调来处理缓冲区大小(即on_drain事件处理程序来指示写入缓冲区何时为空并wbuf_max限制写入缓冲区大小)。

然而,使用这些特性将是非常特定于服务器的,并且会限制 PSGI 应用程序的可移植性。PSGI 规范似乎没有涵盖用于控制/监视异步写入流或访问底层文件句柄/描述符以进行手动检查的 API。

其他人如何解决内存使用/缓冲问题或知道异步写入何时以跨 PSGI Web 服务器“兼容”的方式完成?任何指针都会很棒。

标签: perlpsgi

解决方案


作为后续行动,我想我会发布一个简化版本,说明我如何解决我的问题,以防它帮助其他人。

使用 in 中使用的{handle}元素,我手动设置了和的回调。AnyEvent::Handlewriteron_drainon_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...
    }
}

推荐阅读