Блог веб-разработчика: в помощь программистам

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


Я думаю, что все сталкивались с тем, что при росте посетителей хостинга перестает хватать, странички грузятся по нескольку секунд (десятков секунд). Посетители начинаю жаловаться, что “сайт тормозит”.  Хостер требует перейти на более дорогой тарифный план.

Всего этого можно с легкостью избежать. Об этом и будет серия статей в которых будет описано как ускорить свои сайты на работу с максимальной скоростью на которую они только способны.

К сожалению в силу ряда причин хостеры сильно ограничивают пользователя на виртуальном хостинге. Из-за этого контролировать что либо становится невозможно. По-этому мы будем говорить про хостинг как минимум на VPS. Данный вариант позволит нам самим ставить любое ПО для наших нужд.

Список того, что нам потребуется в ближайшее будущее:

  1. nginx – проксирующий веб сервер с богатыми возможностями.
  2. apache – “стандартный” родной веб сервер.
  3. memcached -мемкэш сервер (о нем остановимся более подробно).
  4. sql – всеми любимый сервер баз данных.
  5. 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 вашего сервера в нужные места.

На сегодня все. В следующей статье мы установим Апач и узнаем как легко создавать новые конфигурации для новых сайтов.

  • ?Download nginx.conf
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 
    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

  • число воркеров nginx до 8 можно довести смело
    keepalive_timeout 0; – не стоит держать соединение, если оно прервалось. клиент может и обновить страничку, а сервера приходится держать лишний порт.

    timer_resolution 100ms; – улучшает отклик системы в целом
    use epoll; – ускоряет работу с файлами в целом

    я еще добавляю
    worker_priority -5; что бы при перегруженном сервере nginx всегда был в приоритете выше, чем апач, что позволяет обрабатывать “черные списки” из бан листа, если сайт ддосят.

  • отлично, спасибо – допишу
    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. Хорошая статья.

    Вот такой предлагаю оптимизированный конфиг на хабре

    ?Download nginx.conf
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 
    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;  }
  • 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.

    Про мемкэш будет отдельная статья и про использование еще одна. Знать такие вещи полезно, тем более, что с моими готовыми кусками кода все становится еще намного проще.

  • Нет, от джумлы я отказался в пользу Drupal, DLE, WordPress

    А скрипт должен поддерживать кеширование с помощью APC что бы APC делал свое дело?

    Потому что для того что бы работал EA поддержку в скрипте иметь не надо

  • WordPress умеет работать с мемкэшем, кстати.

    APC имеет такой же принцип работы, что и AE.

  • а для memcache нужна поддержка на стороне движка?

  • да, конечно.
    Мемкэш сервер – это простой сервер-хранилище данных. Главное его отличие в том, что он хранит данные в виде ключ/данные, а не в таблицах.
    Об всех подобных серверах я думаю надо написать отдельно, так как есть особенности работы, которые ломают мозг тем, кто привык работать только с sql.

  • а как сделать так, чтобы nginx в зависимости от нагрузки обрабатывал часть php-запросов, а другую часть php-запросов передавал на apache?

  • А обработать часть запросов он чем должен?
    Nginx ничего не обрабатывает, он просто служит прокси сервером с “плюшками”.
    В данном случае “плюшки” = модуль работы с мемкэшем

  • Евгений, можно с Вами связаться по ICQ?

    • nginx может php обрабатывать, но при этом еще держать апач и балансить это получается полный бред

      • Nginx сам ничего не может.
        Работать с php может только через fast-cgi механизм (при этом ему пофиг какой язык, в принципе)
        Одновременно задать несколько бэков, с разными механизмами обработки скриптов, в принципе можно, для этого проще всего сделать виртуальный локейшен с виртуальными бэками на самого себя и потом с разных бэков можно вести куда угодно.
        схема дурацкая, но вполне реальная и использовалась для сглаживания нагрузки в критические моменты (первый бэк на этом же сервере, а второй используется только тогда, когда первый не отвечает более 5 раз при таймауте в 10 секунд)

        • а как реализовать всё это?
          просто у нас одна машинка и большой онлайн. для статики используем nginx и фронт-енд апач
          хотелось бы чтобы обрабатывал всё nginx без задержек и ошибок

  • Вы так считаете?
    Но так же возможно? И как если возможно?
    Подскажите пожалуйста!

  • Ну раз сервер один, то о балансировании нагрузки речи не идет вообще.
    Надо смотреть во что упирается все: трафик, процессор, память.
    По трафику все понятно, а вот проц/память, тут надо смотреть уже предметно

    • Можно с Вами как нибудь связаться?

You can follow any responses to this entry through the RSS 2.0 feed.