Ускоряем сайт на PHP (часть №1)

Я думаю, что все сталкивались с тем, что при росте посетителей хостинга перестает хватать, странички грузятся по нескольку секунд (десятков секунд). Посетители начинаю жаловаться, что “сайт тормозит”. Хостер требует перейти на более дорогой тарифный план.
Всего этого можно с легкостью избежать. Об этом и будет серия статей в которых будет описано как ускорить свои сайты на работу с максимальной скоростью на которую они только способны.
К сожалению в силу ряда причин хостеры сильно ограничивают пользователя на виртуальном хостинге. Из-за этого контролировать что либо становится невозможно. По-этому мы будем говорить про хостинг как минимум на VPS. Данный вариант позволит нам самим ставить любое ПО для наших нужд.
Список того, что нам потребуется в ближайшее будущее:
- nginx – проксирующий веб сервер с богатыми возможностями.
- apache – “стандартный” родной веб сервер.
- memcached -мемкэш сервер (о нем остановимся более подробно).
- sql – всеми любимый сервер баз данных.
- munin – система мониторинга сервера.
Сразу скажу. что список будет дополнятся в зависимости от задач и целей, но данная связка позволяет нам успешно оптимизировать наши сайты.
Я думаю, что каждый разработчик уже детально знает nginx+apache+sql, или по меньшей мере пользовался сборками по типу LAMP, XAMMP и т.п. сборкам апача, sql и нескольких утилит для удобной работы.
Основная проблема Apache в том, что он занимает достаточно много оперативной памяти, и достаточно медлителен что бы с помощью его мы отдавали пользователям статические файлы. Для отдачи статики (первоочередная задача) мы поставим Nginx, который будет принимать запросы от посетителей сайта, отдавать им картинки, а обработку php отдавать Apache.

Вот схема которую мы реализуем.
Поясню что здесь просиходит:
- запросы от посетителей сайта принимаются nginx, который принимает их на 80 порту
- Nginx делит запросы на динамические данные и статические
- Статические данные с диска nginx отдает пользователю сам, а динамические данные передает апачу на 81 порт
- Апач принимает запросы на 81 порту и отдает данные nginx на максимальной скорости
- Nginx принятые от Апача данные отдает пользователю
Как мы видим из этой схемы при средней страничке, которая содержит пару css, 3-4 js файла и 20-30 картинок наш Апач отдает только html код, а статика отдается напрямую с диска (что занимает считанные копейки мощности процессора)
Готовы ускорить свой сервере? – Тогда начнем.
Будем считать, что сервер с голой системой и ни чего не стоит. Если у вас уже установлено какое то ПО, то пропустите шаги по его установке.
Установка Nginx
Установка Nginx занимает несколько секунд.
Debian:
apt-get install nginx
FreeBSD:
cd /usr/ports/www/nginx make install clean
Конфигурационный файл nginx вы найдете у себя в
Debian – /etc/nginx/nginx.conf
FreeBSD – /usr/local/etc/nginx/nginx.conf
удаляем все из конфига и вставляем в него:
user www-data;
worker_processes 2;
# число должно быть (желательно)
# равно числу ядер Вашего сервера.
timer_resolution 100ms;
worker_rlimit_nofile 80000;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 65536;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
access_log /dev/null combined;
server_tokens off;
log_format TIME $time_local, $status, $request_time,
$upstream_response_time, $request;
server_names_hash_bucket_size 128;
server_names_hash_max_size 10240;
client_max_body_size 32M;
tcp_nopush on;
tcp_nodelay on;
client_header_timeout 3m;
client_body_timeout 3m;
send_timeout 3m;
reset_timedout_connection on;
client_header_buffer_size 4k;
large_client_header_buffers 4 16k;
gzip off; # отключаем сжатие, о причинах ниже.
gzip_min_length 1024;
gzip_buffers 12 32k;
output_buffers 4 32k;
postpone_output 1460;
keepalive_timeout 0;
connection_pool_size 256;
server {
listen ipвашегосервера:80;
server_name *.* ;
server_name_in_redirect off;
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow ipвашегосервера;
deny all;
}
location / {
proxy_pass http://127.0.0.1:81;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_body_buffer_size 128k;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
proxy_buffering on;
proxy_buffer_size 64k;
proxy_buffers 16 64k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
access_log off;
error_page 502 =502 /502.html;
}
location /502.html {
root /var/www/nginx-default;
}
}
}
И так, у нас стоит nginx.
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow ipвашегосервера;
deny all;
}
Данный блок стоит для того, что бы мы могли снимать статистику в дальнейшем для понимания узких мест нашего сервера.
Как могли заметить многие gzip у меня отключен. Причина на самом деле простая – включая динамическое сжатие мы получаем доп. нагрузку на процессор, но чуть экономим на трафике. в 90% случаев эта экономия будет незначительна, так как сжимаются только html, css и js данные, а картинки остаются неизменными. Глупо тратить мощность процессора на сжатие, что бы сэкономить менее 5% трафика. Главная страница этого блога весит около 450 килобайт, включив сжатие мы “наэкокономим” целых 12 килобайт трафика, но поднимем нагрузку на процессор на 10-15% при загрузке в 80-90%. Я не берусь уговаривать Вас на 100% не использовать сжатие, но делать надо все с умом.
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Этот блок отвечает за правильную передачу ip клиента от nginx к апачу. Дело в том, что для апача все запросы от nginx идут с адреса 127.0.0.1 и если Вы с коде пытаетесь получить ip пользователя, то это не получится.
Прочие параметры в данном конфиге выстраданы кровью и потом на нескольких сильно загруженных сайтах с посещаемостью от 50 тыс “уников” в сутки.
Не забудьте поставить ip вашего сервера в нужные места.
На сегодня все. В следующей статье мы установим Апач и узнаем как легко создавать новые конфигурации для новых сайтов.
Разработка интернет-магазинов, скриптов, SEO и повышение конвертации. Менеджмент онлайн проектов.
admin
17 Jan, 2011
user nginx; worker_processes 2; error_log /var/log/nginx/error.log; # set open fd limit to 30000 worker_rlimit_nofile 30000; events { worker_connections 2048; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] $request ' '"$status" $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log off; server_tokens off; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; gzip on; # large_client_header_buffers 8 32k; client_body_buffer_size 256k; output_buffers 16 32k; proxy_temp_file_write_size 64k; connection_pool_size 256; client_header_buffer_size 32k; large_client_header_buffers 16 16k; request_pool_size 32k;вот такой стоит конфиг nginx’a на сервере где бегает наш сайт
данные подобраны методом проб и ошибок + некоторые оптимизаторские доки
сервер 2×4 ядерный процессора (3ghz) + 12гб RAM
webphp
18 Jan, 2011
число воркеров nginx до 8 можно довести смело
keepalive_timeout 0; – не стоит держать соединение, если оно прервалось. клиент может и обновить страничку, а сервера приходится держать лишний порт.
timer_resolution 100ms; – улучшает отклик системы в целом
use epoll; – ускоряет работу с файлами в целом
я еще добавляю
worker_priority -5; что бы при перегруженном сервере nginx всегда был в приоритете выше, чем апач, что позволяет обрабатывать “черные списки” из бан листа, если сайт ддосят.
admin
18 Jan, 2011
отлично, спасибо – допишу
keepalive_timeout сменить на 0, правильно понял? что бы порт закрывался моментально после передачи
хороший материал http://habrahabr.ru/blogs/nginx/56497/ в дополнение
eaccelerator в этой связке, который рекомендует автор на хабре приводит к падению апача время от времени
Server version: Apache/2.2.14 (Unix)
Server built: Oct 14 2009 13:20:11
при этом так же выяснилось что некоторые скрипты (в частности чат на AJAX), начинают отдавать Gateway 502 ошибку nginx’а. По причине падения апача. Выключение eaccelerator решило эту проблему. Когда рыл инет выяснил что это не такая уж редкая проблема, и что кроется она в баге в eaccelerator-а, который не решен. И перекомпиляция его не помогает. Народ мается перекомпилирует с разными параметрами EA, но ошибка остается.
Интересно прочитать про оптимизацию самого апача в такой связке, memcached. Хорошая статья.
Вот такой предлагаю оптимизированный конфиг на хабре
user nginx; # Число рабочих процессов, рекомендуется ставить по количеству ядер worker_processes 8; # Уменьшает число системных вызовов gettimeofday(), что приводит к увеличению производительности timer_resolution 100ms; # Изменяет ограничение на число используемых файлов RLIMIT_NOFILE для рабочего процесса. worker_rlimit_nofile 8192; # Директива задаёт приоритет рабочих процессов от -20 до 20 (отрицательное число означает более высокий приоритет). worker_priority -5; error_log /var/log/nginx/error.log; pid /var/run/nginx.pid; events { worker_connections 2048; # use kqueue; для freebsd (рекомендация от psihoz) use epoll; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] $request ' '"$status" $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; # Включить sendfile(). Использование sendfile() экономит системные вызовы, уменьшает число копирований данных, # позволяет использовать меньше физической памяти. sendfile on; keepalive_timeout 65; gzip on; gzip_min_length 1100; gzip_buffers 64 8k; gzip_comp_level 3; gzip_http_version 1.1; gzip_proxied any; gzip_types text/plain application/xml application/x-javascript text/css; # Load config files from the /etc/nginx/conf.vs directory include /etc/nginx/conf.vs/*.conf; }webphp
18 Jan, 2011
worker_connections 2048;
и
worker_rlimit_nofile 8192;
можно и больше сделать. По факту, при нормальном vps, 2048 подключений потратятся быстро (1 страничка уже 10-20-30 соединений).
Я не скажу, что мой конфиг идеален, но он максимально готов к ddos. Как раз по этому я выставил keepalive_timeout 0; тут механизм таков, что ddos на сервер может идти не путем запроса всех страниц и получение страницы, а просто отправкой пакетов на запрос и разрыв соединения. А при keepalive_timeout 65;
Nginx еще 65 секунд будет держать порт за пользователем.
т.е. 100 посетителей в секунду и через 65 секунд кончатся все порты.
eaccelerator – сразу скажу, что не люблю его. Отдаю предпочтение APC.
Если ваш сайт сделан на CMS Joomla1.5, то поддержка кеширования при помощи APC уже доступна для вашего сайта. Достаточно просто включить кеширование в “Общих настройках” сайта и выбрать из выпадающего списка способ кеширования: APC.
Про мемкэш будет отдельная статья и про использование еще одна. Знать такие вещи полезно, тем более, что с моими готовыми кусками кода все становится еще намного проще.
admin
18 Jan, 2011
Нет, от джумлы я отказался в пользу Drupal, DLE, WordPress
А скрипт должен поддерживать кеширование с помощью APC что бы APC делал свое дело?
Потому что для того что бы работал EA поддержку в скрипте иметь не надо
webphp
18 Jan, 2011
WordPress умеет работать с мемкэшем, кстати.
APC имеет такой же принцип работы, что и AE.
admin
18 Jan, 2011
а для memcache нужна поддержка на стороне движка?
webphp
19 Jan, 2011
да, конечно.
Мемкэш сервер – это простой сервер-хранилище данных. Главное его отличие в том, что он хранит данные в виде ключ/данные, а не в таблицах.
Об всех подобных серверах я думаю надо написать отдельно, так как есть особенности работы, которые ломают мозг тем, кто привык работать только с sql.
kdn1990
17 Jun, 2011
а как сделать так, чтобы nginx в зависимости от нагрузки обрабатывал часть php-запросов, а другую часть php-запросов передавал на apache?
Евгений
17 Jun, 2011
А обработать часть запросов он чем должен?
Nginx ничего не обрабатывает, он просто служит прокси сервером с “плюшками”.
В данном случае “плюшки” = модуль работы с мемкэшем
kdn1990
17 Jun, 2011
Евгений, можно с Вами связаться по ICQ?
admin
17 Jun, 2011
nginx может php обрабатывать, но при этом еще держать апач и балансить это получается полный бред
Евгений
19 Jun, 2011
Nginx сам ничего не может.
Работать с php может только через fast-cgi механизм (при этом ему пофиг какой язык, в принципе)
Одновременно задать несколько бэков, с разными механизмами обработки скриптов, в принципе можно, для этого проще всего сделать виртуальный локейшен с виртуальными бэками на самого себя и потом с разных бэков можно вести куда угодно.
схема дурацкая, но вполне реальная и использовалась для сглаживания нагрузки в критические моменты (первый бэк на этом же сервере, а второй используется только тогда, когда первый не отвечает более 5 раз при таймауте в 10 секунд)
kdn1990
19 Jun, 2011
а как реализовать всё это?
просто у нас одна машинка и большой онлайн. для статики используем nginx и фронт-енд апач
хотелось бы чтобы обрабатывал всё nginx без задержек и ошибок
kdn1990
18 Jun, 2011
Вы так считаете?
Но так же возможно? И как если возможно?
Подскажите пожалуйста!
Евгений
19 Jun, 2011
Ну раз сервер один, то о балансировании нагрузки речи не идет вообще.
Надо смотреть во что упирается все: трафик, процессор, память.
По трафику все понятно, а вот проц/память, тут надо смотреть уже предметно
kdn1990
21 Jun, 2011
Можно с Вами как нибудь связаться?
Евгений
22 Jun, 2011
zeen@zeen.ru