Эхо-контент иногда занимает очень много времени
У меня есть script, который строит мою веб-страницу в одной строке ($ content), а затем повторяет ее пользователю.
Мой script выглядит следующим образом:
$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-20 кб, а время, когда это происходит, совершенно случайно, поэтому оно не в трудное время и даже происходит посреди ночи.
Кто-нибудь знает, что это может быть?
ИЗМЕНИТЬ
Сайт размещен на удаленном выделенном сервере и размещает только этот сайт. Существует база данных, но, как я уже сказал, $content_time составляет менее 1 секунды, так что эта функция не может быть задержкой.
Когда время моего сайта превышает определенное значение (скажем, 5 с), я регистрирую его. Иногда у Googlebots возникают такие проблемы, поэтому я не думаю, что они используют коммутируемое соединение:)
Ответы
Ответ 1
Позвольте сузить проблему и отбросить некоторые вещи...
В вопросе вы указываете, что вы эхо 10-15kb. Это значительная сумма независимо от того, как она буферизована для вывода - помните, что php - это один поток, как только вы очищаете свой буфер, вам нужно дождаться, когда все выходные данные будут выполнены через оболочку или HTTP до того, как будет продолжен script. В конце концов, он должен будет очистить внутренний буфер, прежде чем продолжить эхо. Чтобы получить хорошее время без сброса над головой эха
Попробуйте заменить
$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_time сжимается, у вас есть проблема с транспортом, с которой вы можете справиться как можно лучше с буферизацией.
Если echo_time все еще велико, вам нужно начать копаться в коде PHP C.
В любом случае вы намного ближе к поиску вашей проблемы и решению
Ответ 2
Из http://wonko.com/post/seeing_poor_performance_using_phps_echo_statement_heres_why
Этот старый отчет об ошибке может пролить свет. Короче говоря, использование эха для отправки больших строк в браузер приводит к ужасной производительности из-за способа "Алгоритм Нэглса" заставляет буферизировать данные для передачи по 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);
}
}
Пробовали ли вы использовать script с помощью CLI, а не через Apache?
Ответ 3
Вы можете сделать это лучше, используя выходные буферы. На базовом уровне вы используете ob_start()
, чтобы начать запись в выходной буфер, а затем ob_end_flush()
, чтобы отправить его клиенту. Вот что php.net должен сказать о ob_start()
:
Эта функция включит буферизацию вывода. В то время как буферизация вывода активна, выходной сигнал не выводится из script (кроме заголовков), вместо этого вывод сохраняется во внутреннем буфере. Содержимое этого внутреннего буфера может быть скопировано в строковую переменную с помощью ob_get_contents()
. Чтобы вывести то, что хранится во внутреннем буфере, используйте ob_end_flush()
.
Ответ 4
У меня была такая же проблема в прошлом, очень похожая на вашу. Я обнаружил, что эта проблема может быть вызвана медленными клиентами. Если клиент взял половину страницы и затем зависает, php будет ждать, пока клиент будет готов, а затем отправит остальную часть содержимого. Таким образом, это может быть не проблема на вашей стороне.
Обновление:
Вы можете попробовать выполнить следующие сценарии на своем сервере, чтобы проверить это. Этот script помещается на ваш сервер и называет его echo.php:
<?php
$time_start = time();
echo str_repeat("a", 200000);
echo "\nThis script took: " . (time() - $time_start) . " sec";
Затем введите его с помощью script (измените 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 секунды для запуска.
Ответ 5
Как невозможно рассказать вам причину, не зная тела вашей функции create_content(), я предлагаю вам добавить больше функций "регистрации времени" непосредственно внутри этой функции. Внесение включенного кода все меньше и меньше, вы, наконец, найдете линию, которая вызывает отставание.
Знание конкретной строки поможет вам понять проблему (база данных, загрузка машины, проблемы с подключением к внешним службам,...).
Ответ 6
Есть ли у вас какое-то время() или for() в вашем script? Если это так, вы должны проверить, не противоречат ли эти значения чему-либо, иногда я забыл об этом самостоятельно, а мой script также работал около 30 секунд.
Ответ 7
Моя догадка заключается в том, что действие доступа к этому большому числу строк занимает приличный объем памяти для нескольких целей. Поскольку PHP - сбор мусора, память берется до тех пор, пока сборщик мусора не будет запущен, а затем он освободится. Я предполагаю, что несколько запросов на сохранение содержимого в строковой переменной приводят к быстрому заполнению энергозависимой памяти (ОЗУ). Затем несколько раз в день вы начинаете поражать лимит, вызывая более медленное время загрузки. Сборщик мусора попадает, и все возвращается в норму.
Ответ 8
Если это выделенный сервер, пожалуйста, войдите в консоль и посмотрите, какой процесс использует много процессорного времени при создании контента. Его очень сложно сказать, когда мы не можем видеть код. Возможно, вам просто нужны индексы в базе данных, или, возможно, вам нужно удалить некоторые индексы.
Вы также можете проверить файлы журнала httpd и mysqld.