我有一个脚本,用一个字符串($ content)构建我的网页,然后将其回显给用户。
我的脚本如下所示:
$time1= microtime(true);
$content = create_content();
$content_time=(microtime(true)-$time1)
$time = microtime(true);
echo $content;
$echo_time = (microtime(true)-$time);
现在$ content_time总是低于0.5秒,所以没问题。然而,每天几次$ echo_time远远超过一秒,甚至可以达到15秒。内容不是很大,大约10-20kb,发生这种情况的时间是完全随机的,所以它不是在忙碌的时候,甚至发生在半夜。
任何人都知道那可能是什么?
EDIT 该站点托管在(远程)专用服务器上,仅托管此站点。有一个数据库涉及,但就像我说$ content_time远低于1秒,所以这个功能不能延迟。
当我的网站时间超过某个值(比如5s)时,我会记录下来。甚至Googlebots似乎也有时会遇到这些问题所以我认为他们不会使用拨号连接:)
答案 0 :(得分:10)
让我们缩小问题范围,并考虑一些事情......
在问题中,你表明你正在呼喊10-15kb。这是一个很大的数量,无论它如何缓冲输出 - 记住php是单线程,一旦你刷新你的缓冲区,你必须等待所有输出通过shell或HTTP发生在脚本继续之前。在继续回声之前,它最终必须刷新内部缓冲区。在没有回声的冲洗开销的情况下获得好时光
尝试更换
$time = microtime(true);
echo $content;
$echo_time = (microtime(true)-$time);
用
ob_start();
$time = microtime(true);
echo $content;
$echo_time = (microtime(true)-$time);
ob_clean();
这将回显缓冲区,但实际上并没有通过HTTP或其他方式将其吐出。这应该给你echo命令的“实际”时间,而不用担心发送缓冲区中的内容。
如果echo_time缩小,您可以通过缓冲来解决传输问题。
如果echo_time仍然很大,你需要开始深入研究PHP C代码。
无论哪种方式,您都可以更接近找到问题和解决方案
答案 1 :(得分:5)
来自http://wonko.com/post/seeing_poor_performance_using_phps_echo_statement_heres_why
This old bug report可能会有所启发。简而言之,使用echo向浏览器发送大字符串会导致可怕的性能,因为Nagle’s Algorithm导致数据被缓冲以通过TCP / IP传输。
解决方案?一个简单的三行函数,在回显之前将大字符串拆分成较小的块:
function echobig($string, $bufferSize = 8192) {
$splitString = str_split($string, $bufferSize);
foreach($splitString as $chunk) { echo $chunk; }
}
使用缓冲区大小,看看哪种方式最适合您。我发现8192,除了是一个很好的圆形数字,似乎是一个很好的大小。某些其他价值观也有效,但经过几分钟的修补后我无法辨别出一种模式,显然有一些数学在工作,我不想试图弄明白。
顺便说一下,当使用PHP的输出控制功能(ob_start()和朋友)时,性能也会受到影响
在OPs发表评论之后,我试过这个,我在PHP.net上发现以下内容,表明str_split也可能浪费资源,并且可以使用以下代码进一步优化echobig功能:
function echobig($string, $bufferSize = 8192) {
// suggest doing a test for Integer & positive bufferSize
for ($chars=strlen($string)-1,$start=0;$start <= $chars;$start += $bufferSize) {
echo substr($string,$start,$buffer_size);
}
}
您是否尝试使用CLI而不是通过Apache运行脚本?
答案 2 :(得分:1)
您可以使用输出缓冲区更好地完成此操作。在基本级别上,使用ob_start()
开始写入输出缓冲区,然后ob_end_flush()
将其推送到客户端。以下是php.net对ob_start()
所说的内容:
此功能将打开输出缓冲。当输出缓冲处于活动状态时,不会从脚本(标头除外)发送输出,而是将输出存储在内部缓冲区中。 可以使用
ob_get_contents()
将此内部缓冲区的内容复制到字符串变量中。要输出存储在内部缓冲区中的内容,请使用ob_end_flush()
。
答案 3 :(得分:1)
我过去遇到的问题和你的问题非常相似。我发现这个问题可能是由于客户端速度慢造成的。如果客户端获取了页面的一半然后挂起,则php将等待客户端准备就绪,然后发送其余内容。所以这可能不是你身边的问题。
您可以尝试在服务器上执行以下脚本来检查此问题。这个脚本放在你的服务器上并称之为echo.php:
<?php
$time_start = time();
echo str_repeat("a", 200000);
echo "\nThis script took: " . (time() - $time_start) . " sec";
然后使用此脚本获取它(将example.com更改为您的域):
<?php
$fp = fsockopen("example.com", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET /echo.php HTTP/1.1\r\n";
$out .= "Host: example.com\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)) {
echo fgets($fp, 5000);
sleep(1);
}
fclose($fp);
}
我echo.php
跑了27秒。当我删除行sleep(1)
时,echo.php
只需2秒即可运行。
答案 4 :(得分:0)
由于无法在不知道create_content()函数主体的情况下告诉您原因,我建议您在此函数中直接添加更多“time logging”函数。使包含的代码越来越少,最终会找到导致延迟的行。 了解特定行将有助于您了解问题(数据库,机器负载,与外部服务的连接问题,......)。
答案 5 :(得分:0)
你的脚本中是否有while()或for()循环?如果是这样,你应该检查这些值是否与任何东西都没有冲突,偶尔我自己也忘了这些,我的脚本也会运行大约30秒。
答案 6 :(得分:0)
我的猜测是访问那么大的字符串的行为在多次使用中占用了相当多的内存。因为PHP是垃圾收集的,所以在垃圾收集器运行之前会占用内存,然后释放它。 我的猜测是,在字符串变量中存储内容的多个请求导致快速填充易失性存储器(RAM)。然后每天几次开始达到极限,导致加载时间变慢。垃圾收集器点击,一切都恢复正常。
答案 7 :(得分:0)
如果这是专用服务器 - 请登录到控制台,看看在生成内容时哪个进程占用了大量的cpu时间。当我们看不到代码时,很难说清楚。也许你只需要在数据库中使用一些索引,或者你应该删除一些索引。
您还可以检查httpd和mysqld日志文件。