国产+高潮+在线,国产 av 仑乱内谢,www国产亚洲精品久久,51国产偷自视频区视频,成人午夜精品网站在线观看

能否支持大文件的輸出,或超大內(nèi)容的輸出

pader

我看了 Workerman 自帶的簡(jiǎn)單的 WebServer,現(xiàn)在輸出文件主要是用 $connection->close(file_get_contents($file)); 來(lái)實(shí)現(xiàn)的。

這里假設(shè)有很大的文件(假設(shè)幾個(gè)G)需要給用戶去下載,PHP 進(jìn)程就會(huì)報(bào)內(nèi)存超出的錯(cuò)誤,這種情況下可能用 nginx 單架一個(gè)靜態(tài)文件的輸出更好。

但是打個(gè)比方我們并不簡(jiǎn)單的是下載一個(gè)文件,而是要輸出某些超大內(nèi)容,可能來(lái)自多個(gè)文件組合,又或者經(jīng)過(guò)計(jì)算的動(dòng)態(tài)超大結(jié)果,再可能需要隱藏真實(shí)文件的 URL 而動(dòng)態(tài)輸出,而在 apache 或者 nginx 下可以使用 x-sendfile 或者讓 php 一段一段的輸出來(lái)實(shí)現(xiàn),但在 Workerman 下除了 $connection->close() 來(lái)輸出內(nèi)容,有什么辦法可以分段輸出嗎?

或者是有別的某些方法來(lái)實(shí)現(xiàn)類似效果?

8398 3 5
3個(gè)回答

walkor 打賞

建議:
要發(fā)送給客戶端的數(shù)據(jù)如果有幾個(gè)G,這幾個(gè)G的數(shù)據(jù)最好存儲(chǔ)在本地磁盤(pán)上,避免占用服務(wù)器內(nèi)存。然后根據(jù)客戶端網(wǎng)絡(luò)數(shù)據(jù)擁堵情況分段載入內(nèi)存并發(fā)送。

注意:簡(jiǎn)單的將大文件分段發(fā)送不能避免內(nèi)存爆的問(wèn)題
假如10個(gè)G的文件發(fā)送給客戶端,客戶端接收速度很慢,雖然服務(wù)端將10G文件分成多個(gè)小文件發(fā)送,但是如果客戶端接收速度遠(yuǎn)遠(yuǎn)低于服務(wù)端發(fā)送速度,仍然會(huì)導(dǎo)致服務(wù)端要發(fā)送的數(shù)據(jù)堆積在發(fā)送緩沖區(qū)中,導(dǎo)致內(nèi)存爆掉。就像客戶端帶寬為10k/S,服務(wù)端以1M/S的速度發(fā)送,仍然會(huì)導(dǎo)致數(shù)據(jù)積壓在服務(wù)器發(fā)送緩沖區(qū)導(dǎo)致內(nèi)存爆掉。
正確的做法應(yīng)該是根據(jù)客戶端網(wǎng)絡(luò)數(shù)據(jù)擁堵情況控制發(fā)送速度。

如何判斷客戶端網(wǎng)絡(luò)數(shù)據(jù)放生擁堵?如何發(fā)送?
workerman提供了網(wǎng)絡(luò)擁堵控制機(jī)制,即 onBufferFull和onBufferDrain事件(具體說(shuō)明參見(jiàn)手冊(cè)),當(dāng)服務(wù)端向客戶端的發(fā)送緩沖區(qū)滿時(shí)(緩沖區(qū)大小可控制 參見(jiàn)手冊(cè))會(huì)產(chǎn)生onBufferFull事件,這時(shí)服務(wù)端應(yīng)該停止向這個(gè)客戶端再發(fā)送數(shù)據(jù)(停止從磁盤(pán)read數(shù)據(jù)到內(nèi)存),因?yàn)閛nBufferFull發(fā)生時(shí)說(shuō)明發(fā)送給客戶端的數(shù)據(jù)發(fā)生擁堵。

而當(dāng)發(fā)送緩沖區(qū)的數(shù)據(jù)全部發(fā)送給客戶端后(發(fā)送緩沖區(qū)空了),將會(huì)放生onBufferDrain事件,這時(shí)服務(wù)端可以繼續(xù)從磁盤(pán)read數(shù)據(jù),繼續(xù)向客戶端發(fā)送。

通過(guò)onBufferFull和onBufferDrain事件可以方便控制網(wǎng)絡(luò)擁堵,既能夠減少內(nèi)存消耗,又能以最快的速度將數(shù)據(jù)發(fā)送給客戶端。

示例:
從磁盤(pán)發(fā)送大文件到客戶端參見(jiàn)下面示例(使用的是http協(xié)議,其它協(xié)議也適用)

<?php
use Workerman\Worker;
require_once './Workerman/Autoloader.php';

$worker = new Worker('http://0.0.0.0:4236');
$worker->onMessage = function($connection, $data)
{
    if($_SERVER == '/favicon.ico')
    {
        return $connection->send("HTTP/1.0 404 Not Found\r\nContent-Length: 0\r\n\r\n", true);
    }
    // 這里發(fā)送的是一個(gè)大的pdf文件,如果是其它格式的文件,請(qǐng)修改下面代碼中http頭
    send_file($connection, "/your/path/xxx.pdf");
};

function send_file($connection, $file_name)
{
    if(!is_file($file_name))
    {
        $connection->send("HTTP/1.0 404 File Not Found\r\nContent-Length: 18\r\n\r\n404 File Not Found", true);
        return;
    }

    // ======發(fā)送http頭======
    $file_size = filesize($file_name);
    $header = "HTTP/1.1 200 OK\r\n";
    // 這里寫(xiě)的Content-Type是pdf,如果不是pdf文件請(qǐng)修改Content-Type的值
    // mime對(duì)應(yīng)關(guān)系參見(jiàn) https://github.com/walkor/Workerman/blob/master/Protocols/Http/mime.types#L30
    $header .= "Content-Type: application/pdf\r\n";
    $header .= "Connection: keep-alive\r\n";
    $header .= "Content-Length: $file_size\r\n\r\n";
    $connection->send($header, true);

    // ======分段發(fā)送文件內(nèi)容=======
    $connection->fileHandler = fopen($file_name, 'r');
    $do_write = function()use($connection)
    {
        // 對(duì)應(yīng)客戶端的連接發(fā)送緩沖區(qū)未滿時(shí)
        while(empty($connection->bufferFull))
        {
            // 從磁盤(pán)讀取文件
            $buffer = fread($connection->fileHandler, 8192);
            // 讀不到數(shù)據(jù)說(shuō)明文件讀到末尾了
            if($buffer === '' || $buffer === false)
            {
                return;
            }
            $connection->send($buffer, true);
        }
    };
    // 發(fā)生連接發(fā)送緩沖區(qū)滿事件時(shí)設(shè)置一個(gè)標(biāo)記bufferFull
    $connection->onBufferFull = function($connection)
    {
        // 賦值一個(gè)bufferFull臨時(shí)變量給鏈接對(duì)象,標(biāo)記發(fā)送緩沖區(qū)滿,暫停do_write發(fā)送
        $connection->bufferFull = true;
    };
    // 當(dāng)發(fā)送緩沖區(qū)數(shù)據(jù)發(fā)送完畢時(shí)觸發(fā)
    $connection->onBufferDrain = function($connection)use($do_write)
    {
        $connection->bufferFull = false;
        $do_write();
    };
    // 執(zhí)行發(fā)送
    $do_write();
}
Worker::runAll();

以上例子親測(cè)ok,請(qǐng)?jiān)囉?/p>

  • 暫無(wú)評(píng)論
pader

懂了,可以多次使用 send 并且利用緩沖區(qū)是否滿,來(lái)控制輸出。

非常感謝 walker 這么細(xì)心的回復(fù)!

  • 暫無(wú)評(píng)論
walkor 打賞

不客氣

  • 暫無(wú)評(píng)論
年代過(guò)于久遠(yuǎn),無(wú)法發(fā)表回答
??