Оптимизация eaccelerator+nginx+apache2+php5+mysql

Опубликовано admin в Пт, 22/04/2011 - 14:39
Оптимизация eaccelerator+nginx+apache2+php5+mysql С тяжелым контентом. На физическом сервере.

На днях ко мне обратился знакомый с просьбой избавить сайт от торможения.

Страницы в пиковое время стали долго загружаться, вплоть до 5 минут на страницу.

Первое с чего нужно начать в таком слукчае — это проверить не является ли это проблемой провайдера через которого клиент заходит на сайт. Интернет — это большая совокупность сетей, в которой вполне путь пакета от дата-центра до клиента может оказаться заторможенным по независящим от сайта причинам. Поэтому проверим скорость загрузки сайта через других провайдеров.

Проблема оказалась не в провайдерах.
Раздаётся достаточно тяжёлый видеоконтент. PHP-скрипты довольно простые, не потребляют много процессорного времени. Это я узнал просматривая вывод linux-команды top.

Рассуждаю дальше. Проблема возникла впервые. Посещаемость увеличилась. Много запросов к дисковой подсистеме. Поскольку много пользователей работают с сайтом параллельно, то винчестер постоянно перепозиционирует свои головки и скорость чтения может феноменально упасть из-за такой неприятной вещи как seek-time.

В добавок как я узнал из вывода команды free из 8 гб свапа использовалось 300 мб, что само по себе очень плохой знак. Запись и подгрузка страниц из свопа может конкретно убивать производительность.

Из вывода той же команды top я узнал, что 70-80% времени процессы находятся в состоянии io — что означает ожидание ввода вывода. В принципе под ввод вывод попадает и сетевая карта, но по отчётам munin пик трафика составлял 70 Мбит, т.е. пиковая производительность не была достигнута.

Сервер как фронтэнд использует nginx и я решил посмотреть, что можно сделать с ним.

По первых я установил
Код:
worker_processes 6;


а было 2. Вообще сервер 8 ядерный, но решил не загружать все ядра процессами nginx и оставить парочку на apache и mysql.

Далее я выставил/проверил чтобы были следующие настройки:

Код:
http {

....

# Включить sendfile(). Использование sendfile() экономит системные вызовы, уменьшает число копирований данных,
# позволяет использовать меньше физической памяти.
sendfile on;

# Сетевое ускорение
tcp_nopush on;
tcp_nodelay on;

# При большом количестве отдачи мелких файлов и медленном винчестере может помочь временное отключение логов. Это уменьшит число перепозиционирований головок
access_log off;

# сжимаем тестовые файлы
gzip on;
# мелочёвку не трогаем
gzip_min_length 1000;
gzip_buffers 16 8k;
# что именно сжимать
gzip_types text/plain text/css text/xml application/x-javascript application/xml application/xhtml+xml;

}

Тяжёлый контент раздаётся в flv и mp4. Вообще раздача видео (говоря по забугорному стриминг/streaming) нуждается в особых настройках. Как я понял, основная фишка стриминга — это возможность со стороны сервера отдавать поток так, чтобы не рвались кадры, если запрос идёт не к самому началу файла.

Для оптимальной раздачи flv-файлов nginx должен быть скомпилирован с опцией --with-http_flv_module.

Вообще также есть специальный модуль для стриминга x264 (mp4), но он не входит в официальную сборку nginx и я не стал с ним экспериментировать.

Далее нужно включить для определённых типов файлов стриминг flv. Примерно это делается так:

Код:
http {
...
server {
...
location ~ \.flv$ {
flv;
}
...
}
...
}

Но в моём случае это не прошло. Дело в том, что на сервере знакомого nginx выполняет роль легковесного фронтенда, а все файлы и запросы всё равно идут через apache. Выглядит это примерно так:

Код:
server {
listen 80;
server_name;
...
location / {
limit_conn one 30;
proxy_pass 127.0.0.1:8128/;
...
}
...
}


Если добавить ещё одну секцию location для стриминга flv

Код:
location ~ \.flv$ {
limit_conn one 2;
proxy_pass 127.0.0.1:8128;
proxy_buffer_size 2m;
...
}


Но ещё лучше заставить nginx отдавать статику напрямую. Например так:

Код:
location /video/ {
root /home/ochev/html/www.cmotru.com/;
}

location ~ /video/.*\.flv$ {
root /home/ochev/html//;
flv;
# один пользователь может смотреть одновременно 2 ролика
limit_conn one 2;
}


Далее я вставил в блок server очень важные настройки для раздачи тяжёлого контента

Код:
# Первый мегабайт скачивается без ограничений по скорости,
# потом отдаётся весь поток данных к одному клиенту со скоростью не более 150 кб в секунду
limit_rate_after 1m;
set $limit_rate 150k;


Иначе у какого-то клиента всё будет летать, а у какого-то тормозить. Я решил всё по-коммунистически поделить поровну )

Далее я изучил php.ini и не нашёл какого-либо модуля отвественного за кеширование скомпилированных php-файлов.

Я поставил eaccelerator со следующими настройками:
Код:
zend_extension="/путь/eaccelerator.so"

Код:
eaccelerator.shm_size="8" ;
Размер shared memory под кеширование скомпилированных скриптов
Код:
eaccelerator.cache_dir="/home/ochev/tmp/eaccelerator" ;
Путь к директории где будут кешироваться скрипты, когда закончится shared memory
Код:
eaccelerator.enable="1" ;
модуль включён
Код:
eaccelerator.optimizer="1" ;
производим оптимизацию при компиляции
Код:
eaccelerator.check_mtime="1" ;
проверяем время модификации файла, чтобы решить берём закешированную версию или производим компиляцию
Код:
eaccelerator.debug="0"
eaccelerator.filter=""
eaccelerator.shm_max="0"
eaccelerator.shm_ttl="0"
eaccelerator.shm_prune_period="0"
eaccelerator.shm_only="0" ;
Когда закончится shared memory используем диск
Код:
eaccelerator.compress="0" ;
Сжимаем скомпилированные скрипты для экономии shared memory


До и после я протестировал главную страницу сайта с помощью утилиты ab, входящей в состав апача.

ab -c 10 -n 20 -t 20 www.cmotru.com/
Параллельное число конкурентных потоков — 10, число запросов 20, максимальное время ожидания ответа — 20 секунд

Time per request снизилось на 0.35 секунды на запрос. Эту оптимизацию в полной мере удалось оценить после установки дополнительной памяти: число обработанных запросов в единицу времени с eaccelerator по сравнению с чистым mod_php выросло в 4,11 раза c 22,53 до 92,67 запросов в секунду.

Потом я просмотрел лог медленных запросов сервера mysql. Он включается опцией --log-slow-queries=имя_файла. Нашёл запрос, который исполнялся иногда до 30 секунд. Конец его был примерно такой:
... ORDER BY RAND(), ...... LIMIT ...
избавился от неиндексироваемой сортировки по RAND() разбив запрос на 2 части по технологии. И запрос стал выполняться доли секунды.

После данных манипуляций с nginx у меня возникло желание посмотреть как раздаются статические файлы, а именно на их заголовки. Всё ли в порядке с кешированием, а именно с заголовками по которым браузер клиента решает вопрос о кешировании?

Заголовки с linux-машины можно посмотреть так:
Код:
curl --head полное_имя_файла_с_http

Или через какой-нибудь онлайн-сервис, например, be1.ru/stat/

При этом нужно обратить внимание на заголовки:
Cache-control — директивы кеширования для браузера
Expires — когда нужно запросить этот файл напрямую, а не брать из кеша
Pragma — устаревший заголовок, примерно с такими же функциями как cache-control
Etag — что-типа хеша файла. Если он изменился, то браузер запрашивает файл снова

Сделав запрос к одной из картинок через я ужаснулся:

Запрашиваемая картинка:

Status: HTTP/1.1 301 Moved Permanently
Server: nginx/0.8.53
Date: Wed, 01 Dec 2010 19:47:26 GMT
Content-type: text/html; charset=iso-8859-1
Connection: keep-alive
Location: www.cmotru.com/userfiles/1209/images/2010/12/ekaterinburg/0.jpg
Cache-control: max-age=3600
Expires: Wed, 01 Dec 2010 20:47:26 GMT
Content-length: 404

Этот ответ заставляет браузер повторно обращаться по другому УРЛ, чтобы получить картинку.
Как видно это происходит по тому, что есть некие правила, которые переписывают все УРЛ так, чтобы они начинались с www.

Дело в том, что в коде сайта многие картинки были прописаны с абсолютными путями, начинающимися с ochevidets.ru даже, если смотришь сайт ab -c 10 -n 20 -t 20 www.cmotru.com/.

Я нашёл неправильные строчки в .htaccess и переписал:
# Было
#RewriteCond %{HTTP_HOST} ^ab -c 10 -n 20 -t 20 cmotru\.com$ [NC]
#RewriteCond %{REQUEST_URI} !^/robots\.txt$
#RewriteRule ^(.*)$ www.cmotru.com/$1 [R=301,L]

#Стало
# Переадресуем ТОЛЬКО контентные страницы ab -c 10 -n 20 -t 20 cmotru.com заканчивающиеся на слэш или главную
RewriteCond %{HTTP_HOST} ^ab -c 10 -n 20 -t 20 cmotru\com$ [NC]
RewriteRule ^((.*/)|)$ www.cmotru.com/$1 [R=301,L]


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

После изменений в .htaccess возвращаемые заголовки у картинок улучшились:

Status: HTTP/1.1 200 OK
Server: nginx/0.8.53
Date: Wed, 01 Dec 2010 19:53:24 GMT
Content-type: image/jpeg
Connection: keep-alive
Last-modified: Wed, 01 Dec 2010 09:25:20 GMT
Etag: "62957-1511b-49655e24e2000"
Accept-ranges: bytes
Content-length: 86299
Cache-control: max-age=864000
Expires: Sat, 11 Dec 2010 19:53:24 GMT

Исчезла необходимость для браузера в повторном запросе.

Кроме того оказалось, что маленькие превьюшки всё время генерируются автоматически из больших.

Я думаю нужно создавать их физически в отдельной папке. Это разгрузит сервер от бессмысленных повторяющихся конвертаций. Позволит их кешировать. В бд завести отдельную ячейку для превью. При удалении ролика удалять и превью.


Что и было сделано.




Источник: http://forum.besthost.by/viewtopic.php?t=2918&sid=c48f667c67c3dfc47860d8889f28e049