У меня сайт с относительно низким трафиком, на который раз в неделю после обновления сайта наблюдается большой всплеск посетителей. Во время этого скачка производительность сайта крайне низкая по сравнению с остальной частью недели. Фактическая нагрузка на серверы остается очень низкой, надежно ниже 10% ЦП и ниже 30% ОЗУ (оборудование должно быть полностью избыточным для того, что мы на самом деле делаем), но по какой-то причине Apache, похоже, не может справиться с количеством запросов. Мы запускаем apache 2.2.3 на RHEL 5.7, ядро 2.6.18-274.7.1.el5, x86_64.
Пытаясь воспроизвести это поведение в нерабочее время с помощью ab, я обнаружил значительное снижение производительности при превышении примерно 256 пользователей. Выполнение теста с наименьшим возможным вариантом использования, который я мог придумать (извлекаемый статический текстовый файл, всего 223 байта), производительность стабильно нормальная при 245 одновременных запросах:
Connection Times (ms)
min mean[+/-sd] median max
Connect: 15 25 5.8 24 37
Processing: 15 65 22.9 76 96
Waiting: 15 64 23.0 76 96
Total: 30 90 27.4 100 125
Percentage of the requests served within a certain time (ms)
50% 100
66% 108
75% 111
80% 113
90% 118
95% 120
98% 122
99% 123
100% 125 (longest request)
Но как только я набираю до 265 одновременных запросов, выполнение некоторых из них начинает занимать абсурдное количество времени:
Connection Times (ms)
min mean[+/-sd] median max
Connect: 13 195 692.6 26 3028
Processing: 15 65 21.3 72 100
Waiting: 15 65 21.3 71 99
Total: 32 260 681.7 101 3058
Percentage of the requests served within a certain time (ms)
50% 101
66% 108
75% 112
80% 116
90% 121
95% 3028
98% 3040
99% 3044
100% 3058 (longest request)
Эти результаты очень стабильны для нескольких прогонов. Поскольку к этому ящику идет другой трафик, я не уверен, где именно будет жесткое отключение, если оно есть, но оно кажется подозрительно близким к 256.
Естественно, я предположил, что это было вызвано ограничением потока в prefork, поэтому я пошел дальше и скорректировал конфигурацию, чтобы удвоить количество доступных потоков и предотвратить ненужное увеличение и сжатие пула потоков:
<IfModule prefork.c>
StartServers 512
MinSpareServers 512
MaxSpareServers 512
ServerLimit 512
MaxClients 512
MaxRequestsPerChild 5000
</IfModule>
mod_status подтверждает, что сейчас у меня 512 доступных потоков
8 requests currently being processed, 504 idle workers
Однако попытка одновременного выполнения 265 запросов по-прежнему дает почти те же результаты, что и раньше.
Connection Times (ms)
min mean[+/-sd] median max
Connect: 25 211 714.7 31 3034
Processing: 17 94 28.6 103 138
Waiting: 17 93 28.5 103 138
Total: 57 306 700.8 138 3071
Percentage of the requests served within a certain time (ms)
50% 138
66% 145
75% 150
80% 161
90% 167
95% 3066
98% 3068
99% 3068
100% 3071 (longest request)
После просмотра документации (и Stack Exchange) я не могу найти дальнейшие настройки конфигурации, чтобы попытаться устранить это узкое место. Что-то мне не хватает? Стоит ли мне искать ответы вне apache? Кто-нибудь еще видел такое поведение? Любая помощь будет принята с благодарностью.
РЕДАКТИРОВАТЬ:
По совету Ладададады я столкнулся с апачем. Я пробовал с -tt и -T несколько раз и не нашел ничего необычного. Затем я попытался запустить strace -c против всех запущенных в данный момент процессов apache и получил следующее:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
22.09 0.317836 5 62128 4833 open
19.91 0.286388 4 65374 1896 lstat
13.06 0.187854 0 407433 pread
10.70 0.153862 6 27076 semop
7.88 0.113343 3 38598 poll
6.86 0.098694 1 100954 14380 read
(... сокращено)
Если я правильно прочитал (и терпите меня, поскольку я не очень часто использую strace), ни один из системных вызовов не может учитывать количество времени, которое занимают эти запросы. Похоже, что узкое место возникает еще до того, как запросы попадают в рабочие потоки.
РЕДАКТИРОВАТЬ 2:
Как предложили несколько человек, я снова запустил тест на самом веб-сервере (ранее тест запускался из нейтрального Интернет-адреса). Результаты были удивительными:
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 11 6.6 12 21
Processing: 5 247 971.0 10 4204
Waiting: 3 245 971.3 7 4204
Total: 16 259 973.3 21 4225
Percentage of the requests served within a certain time (ms)
50% 21
66% 23
75% 24
80% 24
90% 26
95% 4225
98% 4225
99% 4225
100% 4225 (longest request)
Итоговое время аналогично интернет-тесту, но, по-видимому, постоянно немного ниже. хуже при локальном запуске. Что еще более интересно, профиль сильно изменился. Если раньше большая часть времени длительных запросов уходила на «соединение», то теперь узкое место, похоже, связано либо с обработкой, либо с ожиданием. Мне остается подозревать, что это может быть отдельная проблема, которая ранее маскировалась сетевыми ограничениями.
Запуская тест снова с другого компьютера в той же локальной сети, что и хост Apache, я вижу гораздо более разумные результаты:
Connection Times (ms)
min mean[+/-sd] median max
Connect: 1 2 0.8 2 4
Processing: 13 118 99.8 205 222
Waiting: 13 118 99.7 204 222
Total: 15 121 99.7 207 225
Percentage of the requests served within a certain time (ms)
50% 207
66% 219
75% 220
80% 221
90% 222
95% 224
98% 224
99% 225
100% 225 (longest request)
Эти два теста вместе вызывают ряд вопросов, но отдельно от этого теперь есть веские основания для серьезного узкого места в сети, возникающего при определенной нагрузке. Я думаю, что следующие шаги будут посвящены отдельному исследованию сетевого уровня.
Что бы я сделал в этой ситуации, так это бегу
strace -f -p <PID> -tt -T -s 500 -o trace.txt
на одном из ваших процессов Apache во время теста ab, пока вы не зафиксируете один из медленных ответов. Тогда посмотрите trace.txt
.
В -tt
и -T
Параметры дают вам отметки времени начала и продолжительности каждого системного вызова, чтобы помочь идентифицировать медленные.
Вы можете найти один медленный системный вызов, например open()
или stat()
или вы можете быстро позвонить (возможно, несколько) poll()
звонит сразу после него. Если вы найдете тот, который работает с файлом или сетевым подключением (что весьма вероятно), просмотрите трассировку назад, пока не найдете этот файл или дескриптор подключения. Более ранние вызовы того же дескриптора должны дать вам представление о том, что poll()
ждал.
Хорошая идея, глядя на -c
вариант. Удостоверились ли вы, что отслеживаемый вами дочерний элемент Apache за это время обслужил хотя бы один из медленных запросов? (Я даже не уверен, как бы вы это сделали, кроме бега strace
одновременно на всех детей.)
К сожалению, strace
не дает нам полного представления о том, что делает запущенная программа. Он отслеживает только системные вызовы. Внутри программы может произойти многое, для чего не нужно ни о чем просить ядро. Чтобы выяснить, происходит ли это, вы можете посмотреть отметки времени начала каждого системного вызова. Если вы видите значительные пробелы, значит, время идет. Это нелегко исправить, и в любом случае между системными вызовами всегда есть небольшие промежутки.
Поскольку вы сказали, что загрузка ЦП остается низкой, это наверное между системными вызовами не происходит лишних вещей, но это стоит проверить.
Если присмотреться к выходу из ab
:
Внезапный скачок времени отклика (похоже, что время отклика отсутствует где-либо между 150 мс и 3000 мс) предполагает, что где-то происходит определенный тайм-аут, который срабатывает при более чем 256 одновременных соединениях. Можно было бы ожидать более плавной деградации, если бы у вас не хватало оперативной памяти или циклов процессора при нормальном вводе-выводе.
Во-вторых, медленный ab
ответ показывает, что 3000 мс были потрачены в connect
фаза. Почти все они заняли около 30 мс, но 5% заняли 3000 мс. Это говорит о том, что проблема в сети.
Куда ты бежишь ab
из? Можете ли вы попробовать его из той же сети, что и компьютер Apache?
Для получения дополнительных данных попробуйте запустить tcpdump
на обоих концах соединения (желательно с ntp
работает на обоих концах, чтобы вы могли синхронизировать два захвата.) и ищите любые повторные передачи tcp. Wireshark особенно хорош для анализа дампов, потому что он выделяет повторные передачи TCP другим цветом, что упрощает их поиск.
Возможно, также стоит посмотреть журналы любых сетевых устройств, к которым у вас есть доступ. Недавно я столкнулся с проблемой с одним из наших брандмауэров, когда он мог обрабатывать пропускную способность в килобайтах в секунду, но не мог обрабатывать количество пакетов в секунду, которые он получал. Он превысил 140 000 пакетов в секунду. Немного быстрой математики на вашем ab
run наводит меня на мысль, что вы бы видели около 13 000 пакетов в секунду (без учета 5% медленных запросов). Может быть, вы достигли узкого места. Тот факт, что это происходит около 256, может быть чистым совпадением.