我看了 Workerman 自带的简单的 WebServer,现在输出文件主要是用 $connection->close(file_get_contents($file)); 来实现的。
这里假设有很大的文件(假设几个G)需要给用户去下载,PHP 进程就会报内存超出的错误,这种情况下可能用 nginx 单架一个静态文件的输出更好。
但是打个比方我们并不简单的是下载一个文件,而是要输出某些超大内容,可能来自多个文件组合,又或者经过计算的动态超大结果,再可能需要隐藏真实文件的 URL 而动态输出,而在 apache 或者 nginx 下可以使用 x-sendfile 或者让 php 一段一段的输出来实现,但在 Workerman 下除了 $connection->close() 来输出内容,有什么办法可以分段输出吗?
或者是有别的某些方法来实现类似效果?
建议:
要发送给客户端的数据如果有几个G,这几个G的数据最好存储在本地磁盘上,避免占用服务器内存。然后根据客户端网络数据拥堵情况分段载入内存并发送。
注意:简单的将大文件分段发送不能避免内存爆的问题
假如10个G的文件发送给客户端,客户端接收速度很慢,虽然服务端将10G文件分成多个小文件发送,但是如果客户端接收速度远远低于服务端发送速度,仍然会导致服务端要发送的数据堆积在发送缓冲区中,导致内存爆掉。就像客户端带宽为10k/S,服务端以1M/S的速度发送,仍然会导致数据积压在服务器发送缓冲区导致内存爆掉。
正确的做法应该是根据客户端网络数据拥堵情况控制发送速度。
如何判断客户端网络数据放生拥堵?如何发送?
workerman提供了网络拥堵控制机制,即 onBufferFull和onBufferDrain事件(具体说明参见手册),当服务端向客户端的发送缓冲区满时(缓冲区大小可控制 参见手册)会产生onBufferFull事件,这时服务端应该停止向这个客户端再发送数据(停止从磁盘read数据到内存),因为onBufferFull发生时说明发送给客户端的数据发生拥堵。
而当发送缓冲区的数据全部发送给客户端后(发送缓冲区空了),将会放生onBufferDrain事件,这时服务端可以继续从磁盘read数据,继续向客户端发送。
通过onBufferFull和onBufferDrain事件可以方便控制网络拥堵,既能够减少内存消耗,又能以最快的速度将数据发送给客户端。
示例:
从磁盘发送大文件到客户端参见下面示例(使用的是http协议,其它协议也适用)
以上例子亲测ok,请试用
懂了,可以多次使用 send 并且利用缓冲区是否满,来控制输出。
非常感谢 walker 这么细心的回复!
不客气