標籤:長串連 串連 web服務 color 功能 line 設定 今天 gic
在開發過程中常常遇到這樣的需求,類比瀏覽器訪問某介面,並擷取返回資料。我們比較常使用的方法是fsockopen與介面建立串連,然後發出指令,然後通過fgets接受傳回值。
但是我們發現,通過PHP類比提供者往往比瀏覽器訪問同樣的介面慢很多。這個問題困擾過我很久,今天終於找到原因了。我看網上很多朋友有同樣的問題,分享出來供大家參考。
我們常常寫這樣的代碼:
while(!feof($sHnd)) { $line = fgets($sHnd, 4096);}
fgets會擷取檔案描述符$sHnd的當前的4096(也可能是別的常數)個位元組,如果還沒有到4096個位元組遇到分行符號了,則只返回分行符號及分行符號之前的內容。
許多文檔教程裡也都是這麼講的,這段代碼許多情況下也能正常執行。我一步一步跟蹤PHP語句的耗時,發現前面若干次的fgets都很快,最耗時的是最後一次fgets,有時間長度達幾秒,有時間長度達十幾秒。
原來這是伺服器的KeepAlive功能造成的,Apache有這麼一個設定(nginx等其他web伺服器也都有):KeepAlive,如果這個設定置為On,則完成一次請求後,伺服器並不會關閉TCP串連,而是保持串連等待瀏覽器下次發起其他請求時直接利用這個串連。但是當fgets擷取最後一段內容時沒有發現分行符號,也沒有檔案結束標誌(feof()),所以fgets擷取完內容後仍繼續等待,希望遇到分行符號或者其他內容以達到4096個字元。於是,就這樣伺服器和PHP耗上了,互相等待。耗了一會後,伺服器先耗不起了,畢竟伺服器的串連數很寶貴,當串連若干秒沒有活動,就會關掉這個串連(Apache通過KeepAliveTimeout這個選項進行設定,這個值通常為5-15)。伺服器關掉串連之後,PHP這邊的fgets這才失落得返回最後一批內容,提供者過程結束。
清楚了慢的原因就知道瞭解決方案了:
伺服器返回的HTTP頭中包含有內容長度這個屬性,當已接受的內容長度與之相等時,我們就可以斷定:介面內容已經擷取完畢,不必再等了。具體做法是:每次擷取不超過剩餘總長度的內容(min(4096, $leftlength))。剩餘總長度為0時,跳出while(feof($xxxxx))的迴圈。
經過這樣的修改,php通過sock方式提供者速度慢的問題已經基本解決了,但還不夠完美,繼續速度最佳化的思路還在KeepAlive上。
大家都知道提供者的耗時相當一部分是浪費在建立串連上,如果我們需要頻繁調用介面的話,還有很大的最佳化餘地。既然伺服器保持了串連,那如果PHP也把串連儲存下來那是不是就不用建立串連了?答案是肯定的:第一次提供者時使用pfsockopen(pfsockopen與fsockopen唯一的區別就是它建立的是長串連)函數建立與伺服器的串連,在訪問完成後不關掉(fclose)串連,以後的訪問就直接使用這個串連。具體到代碼裡就是:先判斷有沒有串連,如果有,繼續用,如果沒有,建立pfsockopen串連。
另外,如果介面返回內容比較短(比如:小於50字元)的話,還有最佳化的餘地,那就是在HTTP要求標頭的Accept-Encoding中去掉gzip。它的作用是告訴伺服器,我(瀏覽器)可以接受壓縮過的內容,如果伺服器也支援gzip就壓縮後再返回,瀏覽器得到內容後解壓縮再顯示。但是如果內容太短的話,壓縮後體積反而會增加,再加上壓縮、解壓縮的時間,就更加得不償失了。
經過以上幾步,提供者速度應該與瀏覽器一樣,理論上還會稍微快一點點。
關於KeepAlive,可以參考這個專題:HTTP Keep-Alive是什嗎?如何工作?
PHP fsockopen受伺服器KeepAlive影響的解決