<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                假設一個場景: 當用戶需要通過我們提供的下載服務,來下載一個較大的文件(200M-2G)時,我們服務端應該如何來滿足這個服務呢? 且當我們的服務端是采用nginx+php的架構時,該如何解決呢。 作為服務端接口層,我們需要從數據層(可能是云存儲,可能是類似于亞馬遜S3的存儲服務)下載較大文件(200M--2G),然后將下載得到的文件,返回給請求客戶端。 且當我們的服務端接口層是采用nginx+php的架構時,該如何解決呢。 如下圖所示: ![](https://box.kancloud.cn/26bf25aecda7ea0a40f3609e74e2bf9f_518x190.png) 最初的解決方法(但無法徹底解決): 很多同學最初的解決辦法,可能會直接采用curl函數,得到返回內容之后,echo 這個返回內容,結束php程序,然后nginx收到php的echo結果,再返回給請求端。 如下圖所示: ![](https://box.kancloud.cn/dd1e53e83dc8186ff2716b732c3374e8_608x165.png) 這段下載代碼很簡單,如下: $ch = \curl_init($url); \curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 獲取數據返回 \curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); // 在啟用 CURLOPT_RETURNTRANSFER 時候將獲取數據返回 \curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArr); \curl_setopt($ch, CURLOPT_HEADER, 1); \curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); \curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60); \curl_setopt($ch, CURLOPT_TIMEOUT, 60); $output = \curl_exec($ch); echo $output; 這樣做,對下載較小的文件,不會有啥問題。但是當需要下載的文教較大時,就會出現許多錯誤: 1、php進程內存溢出: 由于是將整個文件全部下載,并且放在$output變量里面,該變量是存在于內存中的,所以一旦文件過大,就會導致php內存溢出。(許多同學會想到更改php內存限制,但是這畢竟不是一個長久之計,且不說下載上G的文件,會給服務器帶來多少壓力;如果并發下載,可能會很快將系統內存消耗殆盡,導致服務掛掉) 2、下載時間過長,nginx沒有得到響應,導致nginx向請求端報錯(httpcode 504報錯) 由于按照上述方法下載,只有當下載完成后,才會將結果返回響應給nginx,而由于下載文件較大,耗時過長,整個php進程的執行時間就會較大。nginx在接受到客戶端請求之后,將請求發送給php-cgi(通過fast-cgi運行)程序,然后等待php進程返回,但nginx并不會一直等待,在nginx中有一個配置項,fastcgi_read_timeout,該配置項決定了nginx等待fast-cgi返回超時閾值,默認是60秒。當一個請求超過60秒沒有處理完并且響應的話,nginx就會按照請求超時的邏輯報錯(504 timeout報錯 )。(當然同樣也可以通過修改這個配置項的值來避免超時的出現,但是這仍然不是一種最優的選擇,因為這樣客戶端會等待很久,并且在整個等待過程中,沒有任何響應信息) 如圖所示: ![](https://box.kancloud.cn/be96d96d0e7e1cecca56a4b6277a6744_609x165.png) 所以考慮到通過該方法下載,有至少兩個較為致命的缺陷,我們給出了第二種解決辦法。 第二種解決辦法: 簡單地說:通過curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($ch ,$str))方法,將文件一段一段返回。 具體如下: curl_setopt()函數中,有一個配置叫做:CURLOPT_WRITEFUNCTION,通過這個配置,可以實現,一個curl回調方法:php官方手冊是這樣描述該配置的:A callback accepting two parameters. The first is the cURL resource, and the second is a string with the data to be written. The data must be saved by this callback. It must return the exact number of bytes written or the transfer will be aborted with an error(大致的中文意思就是:該回調接受兩個參數。第一個是cURL資源,第二個是具有要寫入的數據的字符串。數據必須通過此回調保持。它必須寫入確切字節數,否則傳輸將中止并出現錯誤) 這樣的描述可能會讓讀者覺得困惑,別急,我們通過代碼進行分析: $ch = \curl_init($url); \curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); \curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); \curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArr); \curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); \curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60); \curl_setopt($ch, CURLOPT_TIMEOUT, 3600); curl_setopt($ch, CURLOPT_BUFFERSIZE, 20971520); $flag=0; curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($ch ,$str) use (&$flag){ $len = strlen($str); $flag++; $type = \curl_getinfo($ch, CURLINFO_CONTENT_TYPE); if($flag==1){ $size = \curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD); $type = \curl_getinfo($ch, CURLINFO_CONTENT_TYPE); $httpcode = \curl_getinfo($ch, CURLINFO_HTTP_CODE); \header("HTTP/1.1 ".$httpcode); \header("Content-Type: ".$type); \header("Content-Length: ".$size); \header('Content-Transfer-Encoding: binary'); \header('Cache-Control:max-age=2592000'); } echo $str; return $len; }); $output = \curl_exec($ch); 核心部分就在最后一個curl_setopt中,可以看到這里配置了CURLOPT_WRITEFUNCTION,緊接著是一個函數, function($ch ,$str) use (&$flag){ $len = strlen($str); $flag++; if($flag==1){ $size = \curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD); $type = \curl_getinfo($ch, CURLINFO_CONTENT_TYPE); $httpcode = \curl_getinfo($ch, CURLINFO_HTTP_CODE); \header("HTTP/1.1 ".$httpcode); \header("Content-Type: ".$type); \header("Content-Length: ".$size); \header('Content-Transfer-Encoding: binary'); \header('Cache-Control:max-age=2592000'); } echo $str; return $len; } 這個函數就是之前講到的回調函數。 該函數需要傳入兩個參數,也就是上面提到的,cURL資源,以及字符串。curl資源不必贅述,著重說一下字符串$str。 當程序執行到\curl_exec($ch);處時,整個curl網絡請求就開始了,當請求端的數據到達我們服務端的那一刻,數據會被放入一個緩沖區,由于是大文件,所以數據會源源不斷的持續到達,當緩沖區收到一定數量的返回值后,就會調用這個回調函數,并將返回得到的數據傳入這里的$str變量。我們獲取這個$str變量,并將它echo就可以瞬時發送給nginx響應,nginx拿到響應數據,立即會返回到客戶端,這樣就能很好的解決上面提到的兩個致命缺陷,1、php進程內存消耗不會持續增加,2,nginx不會響應超時。回調函數的末尾有一個return $len; 這里的$len是$str的長度;我們需要返回這個長度,也就是之前提到的,必須寫入確切的字符串。其實也很好理解,我們將字符串的長度拋出,下一次調用回調函數時,才能正確定位接下來該從哪里傳值。 另外,我加入了一個flag作為標記,通過use(&flag)的方式(有點類似于c語言中的引用),可將flag作為變量,并在函數中不斷累加,起到標記作用。當flag為1時,我們嘗試去獲取http的返回頭,拿到響應碼,文件類型,文件長度,然后進行拼接成我們想要返回給客戶端的http響應頭信息,這里還加上了緩存時間\header('Cache-Control:max-age=2592000');方便請求端做緩存過期策略。 簡單總結: 整個大文件下載過程,策略就到此結束,該方法可以一舉解決內存占用過大,nginx響應超時,客戶端無響應時間過長三個問題。還需要注意一點,在設置CURLOPT_TIMEOUT,需要適當調大一些,這樣對應耗時較長的超大文件下載,才不會在中途斷開。至于中途斷開該如何進行斷點續傳,我們下一篇章再接著講述。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看