我有一个php网站。由于我正在使用模板引擎而且我总是在“一次性”中执行html,因此我预先获得了html文档的大小。所以我决定设置Content-Length标头以获得更好的性能。如果我没有设置它,则使用分块编码传输文档。
html输出的php代码如下所示:
header('Accept-Ranges: none');
header('Content-Length: '.strlen($content));
echo $content;
我在Chrome,IE,Firefox和Safari的Windows下进行了测试 - 它的工作文件。然而,微软Bing bot(使用bing网站管理员工具)表示该网站没有回应。我决定调查,这是我发现的:
所以Centos 5上的elinks是我发现的唯一一个访问该网站时遇到问题的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
当然,如果它是gzip,那么尺寸会有所不同。 php.ini上的zlib.output_compression为On。我猜这可能是问题所在。输出缓冲也是4096.这很奇怪,因为大多数浏览器在可用时使用压缩。我将在网络浏览器中再试一次。
是浏览器(chrome)还要求压缩,并且响应头中存在gzip:
Content-Length: 15916
Content-Encoding: gzip
view source显示15916个字节。 Chrome可以选择显示原始标题以及解析。可能发生的事情是Chrome实际上在计算之前解压缩数据。听起来很奇怪,但这是GUI web浏览器工作的唯一原因,而一些较低级别的客户端不能
答案 0 :(得分:1)
答案已经存在。 Content-Length
必须是实际发送的大小,压缩'$ content'后的大小。您在view-source上看到的内容大小是自然解压缩的大小。
连接不会停止。您的浏览器正在等待更多数据,但压缩数据大小小于浏览器等待的数据。如果您的服务器最终超时连接,您的浏览器将认为它获得了所有数据并显示它。它适用于wget等,因为它们不发送接受压缩标头,服务器不发送压缩响应。
如果必须,您可以停用压缩,手动压缩并发送$content
以及相应的Content-Encoding
标题。
另一种选择是下载未压缩的页面(使用wget发送Accept-Encoding: gzip
,我猜它不会被解压缩,但即使默认情况下没有启用wget也可能支持压缩,我不知道知道。我知道cURL不支持你可以使用它)并获得响应的大小减去标题(这意味着只有 \r\n\r\n
标题结束序列后的数据的大小)和发送Content-Length
时使用该尺寸。但当然,更改压缩级别或实现(不同的Web服务器/模块或相同Web服务器/模块的不同版本)将改变生成的压缩数据的大小,因此这是一种非常脆弱的方法。
为什么要修改Content-Length
? Php或Web服务器应该处理它。
答案 1 :(得分:1)
没有干净整洁的解决方案。我希望能够用:
设置zlib缓冲区大小zlib.output_compression = 131072
如果我确定页面不会超过128k(未压缩),但是无法获得缓冲区的压缩大小。
因此有两种解决方案:
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已关闭。
即使php手册sais 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/
答案 2 :(得分:0)
我有同样的问题 - 我试图设置Content-Length
标题而没有意识到我在缓冲区内测量的长度将大于实际的GZip输出(是的,它似乎是浏览器挂了)。在我已经解决了我的问题(下面的解决方案)之后,我偶然发现了这个Q& A。
@Etherealone点击一点:
连接不会停止。您的浏览器正在等待更多数据,但压缩数据量小于浏览器等待的数据。
@Etherealone和@NickSoft都有点暗示这一点,但实际上没有说出来:动态生成内容的Content-Length
标题不是必需的,服务器应该发送{{1}标头这告诉浏览器保持连接打开,直到它收到一个零长度的块,这表示内容的结束。
但是,分块传输确实增加了一些开销,所以想要指定Transfer-Encoding: chunked
肯定不会受到伤害。 @NickSoft有正确的想法,但它不一定非常复杂。
所以,如果你坚持使用Content-Length
标题而不是让服务器对内容进行分块,那么你所要做的只是缓冲两次;一次用于压缩,然后再次,以便您可以测量大小并发送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
标题消失了;并且“挂起”的浏览器症状消失了(浏览器获取了所有内容并关闭了连接)。