PHP的fsockopen方式訪問接口慢的緣由與優化方案

在開發過程當中經常遇到這樣的需求,模擬瀏覽器訪問某接口,並獲取返回數據。咱們比較常使用的方法是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就壓縮後再返回,瀏覽器獲得內容後解壓縮再顯示。可是若是內容過短的話,壓縮後體積反而會增長,再加上壓縮、解壓縮的時間,就更加得不償失了。

通過以上幾步,訪問接口速度應該與瀏覽器同樣,理論上還會稍微快一點點。php

http://blog.csdn.net/lzr77/article/details/9704405nginx

http://blog.csdn.net/dazhi_100/article/details/46806519web

相關文章
相關標籤/搜索