簡體   English   中英

PHP和Content-Length標頭連接停頓

[英]php and Content-Length header connection stall

我有一個PHP網站。 由於我使用的是模板引擎,因此我總是以“一次性”方式進行html處理,因此可以預先確定html文檔的大小。 因此,我決定設置Content-Length標頭以獲得更好的性能。 如果未設置,則使用分塊編碼傳輸文檔。

用於html輸出的php代碼如下所示:

header('Accept-Ranges: none');
header('Content-Length: '.strlen($content));

echo $content;

我在Chrome,IE,Firefox和Safari的Windows下對其進行了測試-它可以工作。 但是,Microsoft Bing bot(使用bing網站管理員工具)表示該網站沒有響應。 我決定進行調查,這是我發現的結果:

  • wget在CentOS 5.x和CentOS 6.x上可以正常工作
  • CentOS 6.x上的elinks正常運行
  • CentOS 5.x上的elink 停滯 (版本elinks-0.11.1-6.el5_4.1)

因此,我發現Centos 5上的elinks是唯一訪問網站時遇到問題的http客戶端。 但是我不知道如何從中獲取調試信息。

問題:

  1. 有人可以告訴我如何從elink中獲取調試信息。 是否有原始的http + header重復項? 或某種錯誤日志
  2. 知道為什么停滯發生在一個客戶中而沒有發生在另一個客戶中嗎?
  3. 好吧,最有可能是引起問題的錯誤標題“ Content-Length”,因為當我刪除它時,它在elinks和Bing中都可以正常工作。 什么可能導致內容長度差異
  4. 還有其他HTTP客戶可以測試嗎?

所有測試都在相同的Web服務器,相同的php版本,相同的網頁和相同的內容上進行。 我能想到的是UTF-8文本文件標識符(某些瀏覽器放置在文本文件前面的幾個字節)

這是帶有wget的標頭轉儲:

wget dev.site.com/ --server-response -O /dev/null
--2013-11-09 01:32:37--  http://dev.site.com/
Resolving dev.site.com... 127.0.0.1
Connecting to dev.site.com|127.0.0.1|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 200 OK
  Date: Fri, 08 Nov 2013 23:32:37 GMT
  Server: Apache
  Set-Cookie: lng=en; expires=Wed, 07-May-2014 23:32:37 GMT; path=/; domain=dev.site.com
  Last-Modified: Fri, 08 Nov 2013 23:32:37 GMT
  Cache-Control: must-revalidate, post-check=0, pre-check=0
  Pragma: no-cache
  Expires: 0
  Set-Cookie: PHPSESSID=8a1e9b871474b882e1eef4ca0dfea0fc; expires=Thu, 06-Feb-2014 23:32:37 GMT; path=/
  Content-Language: en
  Set-Cookie: hc=1518952; expires=Mon, 17-Nov-2036 00:38:00 GMT; path=/; domain=dev.site.com
  Accept-Ranges: none
  Content-Length: 16970
  Keep-Alive: timeout=15, max=100
  Connection: Keep-Alive
  Content-Type: text/html; charset=UTF-8
Length: 16970 (17K) [text/html]
Saving to: “/dev/null”

100%[===================================================================================================================================================================================================>] 16,970      --.-K/s   in 0.1s

2013-11-09 01:32:37 (152 KB/s) - “/dev/null” saved [16970/16970]

更新:

我只能在生產服務器上重現該問題。 我注意到有效的和無效的elink之間的區別是無效的發送此標頭:Accept-Encoding:gzip

當然,如果壓縮后大小會有所不同。 zlib.output_compression在php.ini上為On。 我想可能是問題所在。 輸出緩沖也為4096。這很奇怪,因為大多數瀏覽器在可用時都使用壓縮。 我將在網絡瀏覽器中重試。

是的,瀏覽器(chrome)也要求壓縮,並且響應頭中存在gzip:

Content-Length: 15916
Content-Encoding: gzip

視圖源精確顯示了15916字節。 Chrome瀏覽器可以選擇顯示原始標題並進行解析。 可能發生的情況是Chrome實際上在計數之前先解壓縮數據。 聽起來很奇怪,但這是GUI Web瀏覽器工作而某些較低級別的客戶端不工作的唯一解釋

答案已經在那里。 Content-Length必須是實際發送的大小即'$ content'壓縮的大小。 您在視圖源上看到的內容大小自然是解壓縮后的大小。

連接不會停止。 您的瀏覽器正在等待更多數據,但是壓縮數據的大小小於瀏覽器正在等待的數據。 如果服務器最終使連接超時,則瀏覽器將假定它已獲取所有數據並顯示出來。 它與wget等兼容,因為它們不發送accept-compression標頭,並且服務器也不發送壓縮響應。

如果需要,可以禁用壓縮,手動壓縮並發送$content以及適當的Content-Encoding標頭。

另一個選擇是下載未壓縮的頁面(發送帶有wget的Accept-Encoding: gzip ,我猜它不會被解壓縮,但是即使默認情況下未啟用它,wget畢竟也可能支持壓縮。我不知道。知道cURL不支持它,您可以使用它)並獲取響應的大小減去標頭(這意味着\\r\\n\\r\\n標頭結束序列之后的數據大小),並在發送Content-Length 但是,當然改變壓縮級別或實現方式(不同的Web服務器/模塊或同一Web服務器/模塊的不同版本)會更改最終壓縮數據的大小,因此這是一種非常脆弱的方法。

您為什么仍要修改Content-Length PHP或Web服務器應該處理。

沒有很好的解決方案。 我希望能夠通過以下方式設置zlib緩沖區大小:

zlib.output_compression = 131072

如果我確定頁面不會超過128k(未壓縮),但是無法獲得緩沖區的壓縮大小。

因此,有兩種解決方案:

  1. 關閉輸出壓縮或不設置Content-Length ...雖然解決方案不多,但是可以
  2. 將zlib壓縮處理程序替換為:

ob_start(); // start normal buffer
ob_start("ob_gzhandler"); // start gzip buffer
echo $content;
ob_end_flush(); // output gzipped content

$gzippedContent = ob_get_contents(); // store gzipped content to get size
header('Content-Length: '.strlen($gzippedContent));
ob_end_flush(); // flush gzipped content

但是請確保zlib.output_compression為Off。

即使php手冊說首選zlib.output_compression,我仍懷疑使用ob_gzhandler會大大降低性能。

您可以通過以下方式設置壓縮級別

ini_set('zlib.output_compression_level', 4);

我對其進行了測試,並且可以在客戶端/瀏覽器中啟用gzip並禁用gzip。

wget --header='Accept-Encoding: gzip,deflate' -O ./page.html.gz http://www.site.com/ && gunzip page.html.gz
wget -O ./page.html http://www.site.com/

我遇到了同樣的問題-我嘗試設置Content-Length標頭,卻沒有意識到我在緩沖區內測量的長度將大於實際GZip輸出的長度(是的,似乎瀏覽器已掛起)。 我已經解決了我的問題(下面的解決方案)后,偶然發現了這個問答。

@Etherealone獨具一格:

連接不會停止。 您的瀏覽器正在等待更多數據,但是壓縮數據的大小小於瀏覽器正在等待的數據。

@Etherealone和@NickSoft都暗示了這一點,但實際上並沒有說明:動態生成內容的Content-Length頭不是必需的,服務器應該發送Transfer-Encoding: chunked頭。 這告訴瀏覽器保持連接打開,直到接收到零長度的塊為止,該塊表示內容的結尾。

但是,對傳輸進行分塊確實會增加一些開銷,因此想要指定Content-Length肯定不會受到傷害。 @NickSoft的想法是正確的,但不必太復雜。

因此,如果您堅持擁有Content-Length標頭而不是讓服務器對內容進行分塊,那么您要做的就是緩沖兩次。 一次進行壓縮,然后再次進行壓縮,因此您可以測量大小並發送Content-Length標頭:

<?php

// "Outer" buffer to capture content and size of "inner" buffer and send content length header
ob_start();

// "Inner" buffer for compression
ob_start('ob_gzhandler');

// Do stuff...
echo $content;

// Flush the inner buffer, the contents of which is GZip'd
ob_end_flush();

// Measure the inner buffer size and set the header
header('Content-Length: ' . ob_get_length());  

// Send the outer buffer
ob_end_flush();

?>

實現此功能后,我看到了新的Content-Length標頭; Transfer-Encoding: chunked標題消失了; 並且“掛起”瀏覽器症狀消失了(瀏覽器獲取了所有內容並關閉了連接)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM