2015-12-19 2 views
3

Я профилировал производительность своего веб-сайта PHP и был удивлен, обнаружив, что шея бутылки была функцией header.Почему функция header() очень медленная?

Я работаю на PHP 5.3 и Apache 2.4.

Я сравнивал два простых файла с ab и обнаружил, что первый - исполняющий phpinfo() - был значительно быстрее второго - вызывал header.

Первый файл (способный работать на более чем 1000 запросов в секунду): (! Способен только 12 запросов в секунду)

<?php phpinfo(); ?> 

Второй файл:

<?php header('HTTP/1.1 200 OK'); ?> 

Полный ab выход из первая проверка:

C:\work\apache24\bin>ab -n 1000 -c 200 http://q.localhost/test.php 
This is ApacheBench, Version 2.3 <$Revision: 1663405 $> 
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 
Licensed to The Apache Software Foundation, http://www.apache.org/ 

Benchmarking q.localhost (be patient) 
Completed 100 requests 
Completed 200 requests 
Completed 300 requests 
Completed 400 requests 
Completed 500 requests 
Completed 600 requests 
Completed 700 requests 
Completed 800 requests 
Completed 900 requests 
Completed 1000 requests 
Finished 1000 requests 


Server Software:  Apache/2.4.16 
Server Hostname:  q.localhost 
Server Port:   80 

Document Path:   /test.php 
Document Length:  69600 bytes 

Server Software:  Apache/2.4.16 
Server Hostname:  q.localhost 
Server Port:   80 

Document Path:   /test.php 
Document Length:  69600 bytes 

Concurrency Level:  200 
Time taken for tests: 0.984 seconds 
Complete requests:  1000 
Failed requests:  0 
Total transferred:  69768000 bytes 
HTML transferred:  69600000 bytes 
Requests per second: 1015.82 [#/sec] (mean) 
Time per request:  196.885 [ms] (mean) 
Time per request:  0.984 [ms] (mean, across all concurrent requests) 
Transfer rate:   69210.84 [Kbytes/sec] received 

Завершить ab результат с th е второй тест:

C:\work\apache24\bin>ab -n 1000 -c 200 http://q.localhost/test.php 
This is ApacheBench, Version 2.3 <$Revision: 1663405 $> 
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 
Licensed to The Apache Software Foundation, http://www.apache.org/ 

Benchmarking q.localhost (be patient) 
Completed 100 requests 
Completed 200 requests 
Completed 300 requests 
Completed 400 requests 
Completed 500 requests 
Completed 600 requests 
Completed 700 requests 
Completed 800 requests 
Completed 900 requests 
Completed 1000 requests 
Finished 1000 requests 

Server Software:  Apache/2.4.16 
Server Hostname:  q.localhost 
Server Port:   80 
Document Path:   /test.php 
Document Length:  0 bytes 
Concurrency Level:  200 
Time taken for tests: 80.099 seconds 
Complete requests:  1000 
Failed requests:  0 
Total transferred:  168000 bytes 
HTML transferred:  0 bytes 
Requests per second: 12.48 [#/sec] (mean) 
Time per request:  16019.840 [ms] (mean) 
Time per request:  80.099 [ms] (mean, across all concurrent requests) 
Transfer rate:   2.05 [Kbytes/sec] received 

Простой вызов функции header приносит производительность вплоть до 12 запросов в секунду. Меня это потрясло.

Почему функция header() работает так медленно, и есть ли что-нибудь, что я могу изменить в своей конфигурации, чтобы исправить ее?

+0

Очень интересно ... Я могу воспроизвести это на моей машине (Ubuntu). * Не * делать с отсутствием выхода тела; ' 'быстрый, но' 'все еще медленный. –

+0

Что произойдет, если вы добавите в свой код следующее? 'Заподлицо(); ob_flush(); ' – Paolo

+0

@Paolo не помогает; вообще не изменяет результаты. (Кстати, логически эти флеши должны быть наоборот, то есть 'ob_flush(); flush()', так как буфер ob_ доходит до буфера записи. Однако это тоже не помогает.) –

ответ

2

Обновление: в 2017 году я, наконец, добрался до подачи a bug report.


я могу воспроизвести это поведение на своей машине (на Apache 2.4.12, используя либо PHP 5.6 или PHP 7.0 - поведение является одинаковым для обоих) и поделиться своим «шок» при виде одного вызова до header() уменьшают производительность скрипта на два порядка. Я до сих пор не знаю, что происходит; это определенно не должно быть.

Однако, я могу по крайней мере, предложить, как это исправить:

  • На PHP 5.4 и выше, используйте http_response_code для установки кодов состояния вместо header.
  • Если вы находитесь в ужасном положении застряли на PHP 5.3, попробуйте использовать нижний регистр http в аргументе, переданном header вместо верхнего регистра HTTP. например

    <?php header('http/1.1 200 OK'); ?> 
    

    На моей машине, это по-прежнему устанавливает код статуса (как the docs примечание, особый случай логики в header для установки кода состояния нечувствительно к регистру), но делает проблему производительности исчезают.

В случае, если вам интересно, да, такое поведение gibberingly с ума, даже по сравнению с низким базовым уровнем вменяемость я ожидаю от PHP. Я собираюсь изучить немного больше и посмотреть, смогу ли я понять, что происходит, но, надеюсь, это, по крайней мере, позволит вам и всем остальным, испытывающим эту проблему, устранить их проблемы с производительностью.

+2

Apache/2.4.17 (Fedora) PHP/5.6.16 - та же проблема. Это ... просто ... слова терпят неудачу. Это безумие, но мне было бы любопытно узнать причины. Это означает, что мы должны изменить [некоторые популярные библиотеки] (https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/Response.php#L340) соответственно? – Federkun

1

Я просто попытался с

ab -n 1000 -c 200 -H "Connection:close" http://localhost/test.php 

где test.php содержит

<?php header('HTTP/1.1 200 OK'); ?> 

и выход:

This is ApacheBench, Version 2.3 <$Revision: 1706008 $> 
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 
Licensed to The Apache Software Foundation, http://www.apache.org/ 

Benchmarking localhost (be patient) 
Completed 100 requests 
Completed 200 requests 
Completed 300 requests 
Completed 400 requests 
Completed 500 requests 
Completed 600 requests 
Completed 700 requests 
Completed 800 requests 
Completed 900 requests 
Completed 1000 requests 
Finished 1000 requests 


Server Software:  Apache/2.4.17 
Server Hostname:  localhost 
Server Port:   80 

Document Path:   /test.php 
Document Length:  3 bytes 

Concurrency Level:  200 
Time taken for tests: 0.364 seconds 
Complete requests:  1000 
Failed requests:  0 
Total transferred:  206000 bytes 
HTML transferred:  3000 bytes 
Requests per second: 2744.74 [#/sec] (mean) 
Time per request:  72.867 [ms] (mean) 
Time per request:  0.364 [ms] (mean, across all concurrent requests) 
Transfer rate:   552.16 [Kbytes/sec] received 

Connection Times (ms) 
       min mean[+/-sd] median max 
Connect:  0 4 6.7  0  19 
Processing:  1 66 72.9  34  330 
Waiting:  0 33 51.3  16  306 
Total:   1 69 74.7  37  348 

Percentage of the requests served within a certain time (ms) 
    50%  37 
    66%  87 
    75% 110 
    80% 129 
    90% 153 
    95% 219 
    98% 334 
    99% 347 
100% 348 (longest request) 

Таким образом, кажется, что отправка Connection: close заголовок разлагает вопрос. Вы должны использовать ab с флагом -k. Это делает функциональность keep-alive, которую браузер делает по умолчанию. С:

ab -n 1000 -c 20 -k http://localhost/test.php 

Время погрузки - это нормально.

Но не спрашивайте меня, почему выход ab -n 1000 -c 200 http://q.localhost/test.php где test.php содержит <?php header('HTTP/1.1 200 OK'); ?> отличается от <?php header('http/1.1 200 OK'); ?>, потому что на самом деле я понятия не имею.

+0

Да, с «keep-alive» времена нормальные, поэтому это означает, что если сайт с этим заголовком, он должен быть быстрым в обычном браузере, но он может быть очень медленным для «ненормального» соединения, например. сканер или специальный браузер без «сохранения», верно? – henry

Смежные вопросы