Комментарии

Галерея

Опрос

Из каких стран идет больше всего спама, попыток взлома на ваши серверы?:

Повышение быстродействия Drupal (nginx, php-fpm, xcache)

Аватар пользователя isn

Озадачившиь необходимостью повышения быстродействия сайта на Drupal 6, пришлось перерыть множество ресурсов в поисках рекомендаций. Данная cms помимо больших возможностей и гибкости конфигурирования отличается еще и высокими требованиями к ресурсам - активно использует множество запросов к базе данных, имеет достаточно тяжелые скрипты. Что, впрочем, не мешает ее применению на высокопосещаемых ресурсах. Однако каких-либо впечатляющих результатов быстродействия невозможно достичь без комплексной и детальной настройки.
Нижеприведенные рецепты во многом применимы и для Drupal 7.

Mysql

Если с чего и стоит начать так это с настройки базы данных. Без этого все другие методы повышения быстродействия не имеют смысла. Все дело в том, что настройки Mysql по умолчанию позволяют ей начать работать на любом обрудовании. Однако о каком-либо приемлемом уровне производительности в таком виде речь не идет. Это связано с тем, что настройки сильно связаны как с вычислительными мощностями оборудования (процессор, количество памяти), так и с типом хранимых данных. Поэтому и не существует универсального рецепта настройки.

В Интернете много статей по тюнингу Mysql. Чтобы в очередной раз не капать из пипетки в океан, приведу ссылку на наиболее грамотное и конкретное, по моему мнению, howto: MySQL-тюнинг. Настраиваем по-взрослому. От себя только добавлю, что рекомендации датированы 2008 годом и в новых версиях названия некоторых опций могут отличаться.
Проконтроллировать результат и точнее подобрать параметры поможет скрипт mysqltuner.pl.

Web-сервер

Обычный вариант установки Drupal подразумевает использование веб-сервера Apache. В этом случае гарантируется максимальное использование возможностей cms и удобства настройки. Модель рабочего процесса для Apache по умолчанию apache-prefork. Это достаточно стабильная модель работы сервера, но не использующая механизм потоков (threads).
С целью улучшения производительности нередко рекомендуют вместо apache-prefork использовать apache-worker - мультипроцессорно-мультипоточную модель. Однако в этом случае теряется возможность обработки php с помощью модуля apache (mod_php), который не поддерживает работу с worker-моделью. Выполнение скриптов возможно в режиме cgi, fastcgi и т.п.
В моем случае разницы во времени генерации одиночной страницы практически не было. Очевидно, что apache-worker будет иметь преимущество только при множестве одновременных запросах.

Другой часто рекомендуемый метод оптимизации - установка веб-сервера nginx в качестве фронтэнда к Apache. При этом статические запросы обрабатывает nginx, а выполнение скриптов поручается делать Apache. Такая связка призвана разгрузить Apache от работы со статикой, улучшив тем самым производительность.

Сочетание apache+nginx часто используется для проектов с высокой посещаемостью. Естественно возникло желание попробовать nginx. И тут возник вопрос: если обработкой статики будет заниматься nginx, php будет исполняться как fastcgi, то что в таком случае остается Apache? Неужели только ради удобства с .htaccess и привычного mod_rewrite следует запускать такой навороченный сервер? Поэтому было принято решение использовать nginx в качестве единственного веб-сервера.

Поскольку в качестве web-сервера выбор пал на nginx, то обработка php-скриптов будет осуществляться в режиме fastcgi. Запуском и управлением процессами php занимается FPM (FastCGI Process Manager), доступный с версии php-5.3.3 (и в виде патча в более ранних).

Примерный конфиг выглядит следующим образом:

;; /etc/php5/fpm/php-fpm.conf
 
[global]
pid = /var/run/php-fpm.pid
error_log = /var/log/php-fpm.log
syslog.facility = daemon
syslog.ident = php-fpm
log_level = warning
process.max = 128
process.priority = -10
events.mechanism = epoll
 
[www]
user = wwwrun
group = www
listen = 127.0.0.1:9000
listen.owner = wwwrun
listen.group = www
listen.mode = 0666
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 500
slowlog = /var/log/$pool.log.slow
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
 
;; здесь можно задать параметры php для каждого пула аналогично php.ini
php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/fpm-php.www.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 512M
php_admin_value[date.timezone] = 'Europe/Moscow'
php_admin_value[upload_max_filesize] = 16M
php_admin_value[post_max_size] = 16M
php_admin_value[cgi.fix_pathinfo] = 0
php_admin_value[expose_php] = off
php_admin_value[magic_quotes_gpc] = 0
php_admin_value[max_execution_time] = 60
php_admin_value[max_input_time] = 120
php_admin_value[allow_url_fopen] = off

На самом деле опций в php-fpm.conf намного больше. Выше приведены только те, что отличаются от умолчальных.

Пример настройки Nginx для работы с Drupal 6. Поскольку сайтов может быть несколько, стандартный конфиг /etc/nginx/nginx.conf удобно разбить на части, включая нужные при необходимости. Так в моем примере nginx.conf содержит общие настройки и сервер по умолчанию, common.conf - часто повторяющиеся настройки для каждого сайта, файл access.conf содержит белый список адресов при ограниченном доступе, drupal.conf - специальные настройки именно для друпала. И в конфиге виртуального хоста vhosts.d/site_name.tld.conf все это собирается воедино.

############### /etc/nginx/nginx.conf ############### 
worker_processes  3;
worker_priority  -10;
error_log  /var/log/nginx/error.log crit;
timer_resolution 100ms;
worker_rlimit_nofile 8192;
 
events {
worker_connections  4000;
use epoll;
multi_accept on;
}
 
http {
include mime.types;
default_type  application/octet-stream;
access_log  off;
 
open_file_cache max=10000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
 
sendfile  on;
directio 128k;
directio_alignment 4K; # for XFS filesystem
output_buffers 1 512k;
tcp_nopush  on;
keepalive_timeout 30;
keepalive_requests 100000;
reset_timedout_connection on;
client_max_body_size 16M;
client_body_timeout 10;
send_timeout 2;
access_log off;
gzip_static on;
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;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;
 
 server {
 listen 80;
 server_name  localhost;
 charset utf-8;
 access_log  off;
 
  location / {
root /srv/www/htdocs/;
index  index.html index.htm;
  }
error_page500 502 503 504  /50x.html;
location = /50x.html {
root /srv/www/htdocs/;
  }
}
 
include vhosts.d/*.conf;
}
############### /etc/nginx/common.conf ############### 
 
## убираем www из имени сайта
if ($host ~* www\.(.*)) {
 set $host_without_www $1;
 rewrite ^(.*)$ http://$host_without_www$1 permanent;
}
## разрешаем только необходимые методы
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 444;
}
## можно запретить доступ ботам-реферальщикам
if ( $http_referer ~* (babes|forsale|girl|jewelry|love|nudit|organic|poker|porn|sex|teen) )
{
return 403;
}
 
## Запретим доступ к файлам начинающимся с точки 
location ~ (^|/)\. {
return 403;
}
 
location = /favicon.ico {
log_not_found off;
access_log off;
}
 
##  Запретим доступ к логам и текстовым файлам с адресов вне белого списка 
location ~* \.(txt|log)$ {
include access.conf;
}
 
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
 
##  Запретим доступ к php-файлам выше корневого каталога сервера
location ~ \..*/.*\.php$ {
return 403;
}
############### /etc/nginx/access.conf ###############
## т.н. белый список адресов 
allow 192.168.0.1/24;
allow 127.0.0.1/32;
deny all;
############### /etc/nginx/drupal.conf ############### 
## Чистые ссылки для друпал
location / {
try_files $uri @rewrite;
default_type  text/html;
log_not_found off;
}
 
location @rewrite {
# You have 2 options here
# For D7 and above:
# Clean URLs are handled in drupal_environment_initialize().
#rewrite ^ /index.php;
# For Drupal 6 and bwlow:
# Some modules enforce no slash (/) at the end of the URL
# Else this rewrite block wouldn't be needed (GlobalRedirect)
rewrite ^/(.*)$ /index.php?q=$1;
# Drupal in a subdirectory
#rewrite ^/([^/]*)/(.*)(/?)$ /$1/index.php?q=$2&$args;
}
 
## Ограничиваем доступ к cron.php
location = /cron.php {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_script_name;
include access.conf; 
}
 
## Обрабатываем php-скрипты через php-fpm 
location ~ \.php$ {
include fastcgi_params;
fastcgi_split_path_info ^(.+\.php)(/.+)$; 
#NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
fastcgi_intercept_errors on;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_script_name;
}
 
## Требуется для работы imagecache
# This is for Drupal 6
location ~ ^/sites/.*/files/imagecache/ {
# This is for Drupal 7 and 8
#location ~ ^/sites/.*/files/styles/ {
try_files $uri @rewrite;
expires 1y;
}
 
## Позволяем использовать кэш браузера клиента для статичных файлов
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 1y;
log_not_found off;
if_modified_since before;
access_log off;
}
############### /etc/nginx/vhosts.d/site_name.tld.conf ############### 
server {
## можно перечислить через пробел все имена сайта, аналогично алиасам в апаче
server_name site_name.tld;
 
include common.conf;
 
## Путь к каталогу друпала
root /home/web_user/drupal;
listen 80;
charset utf-8;
 
## Путь к логу доступа
access_log /home/web_user/log/site_name.tld-access.log;
error_page 404 /404.html;
error_page 403 /403.html;
 
include drupal.conf;
 
}

Подробное описание всех опций см. на официальном сайте и вики.

Кэширование

Чтобы не выполнять одно и тоже существуют кэши.
1. Результат интерпретации php-скриптов - кэш операционного кода.
2. Результаты выдачи БД - кэш запросов.

В качестве первого обычно используют php-акселераторы.
В Drupal имеется стандартный кэш запросов, настраиваемый в admin/settings/performance. Однако эффективность его работы не лишена нареканий. Поэтому в качестве меры по повышению быстродействия рекомендуется сменить стандартное кэширование на что-то более эффективное.
Поиск простых и лаконичных решений привел к xcache. Дело в том, что xcache умеет кэшировать не только операционный код скриптов, но и запросы. Тем самым решаются сразу две задачи.
Устанавливается xcache как расширение PHP, примерная конфигурация выглядит следующим образом:

;; /etc/php5/conf.d/xcache.ini
[xcache-common]
extension = xcache.so
[xcache]
xcache.shm_scheme =        "mmap"
xcache.size  =               192M
xcache.count =                 4
xcache.slots =                8K
xcache.ttl   =                 36000
xcache.gc_interval =           3600
xcache.var_size  =            24M
xcache.var_count =             2
xcache.var_slots =            8K
xcache.var_ttl   =             36000
xcache.var_maxttl   =          604800
xcache.var_gc_interval =     300
xcache.var_namespace_mode =    0
xcache.var_namespace =        ""
xcache.readonly_protection = Off
xcache.mmap_path =    "/var/tmp/xcache"
xcache.disable_on_crash =    Off
xcache.experimental =        Off
xcache.cacher =               On
xcache.stat   =               On
xcache.optimizer =           On
 
[xcache.coverager]
xcache.coverager =           Off

xcache.size - размер кэшей опкода,
xcache.count - количество кэшей (по числу ядер процессора),
xcache.ttl - время жизни данных, если к ним не обращались (секунд),
xcache.gc_interval - интервал сборки мусора (секунд);
xcache.var_size - размер кэшей запросов и т.д.

Для применения настроек перезапускаем php-fpm.
Чтобы drupal вместо стандартного кэша запросов использовал возможности xcache, используется модуль cacherouter. Включаем его на странице модулей admin/build/modules, в файл настроек сайта sites/sitename.tld/settings.php добавляем следующее:

$conf['cache_inc'] = './sites/all/modules/cacherouter/cacherouter.inc';
$conf['cacherouter'] = array(
	'default' => array(
		'engine' => 'xcache',
		'servers' => array(),
		'shared' => TRUE,
		'prefix' => 'some_unique',
		'static' => FALSE,
		'fast_cache' => TRUE,
	),
);

Tweaks and Tricks

Другие необходимые действия, которые обязательно следует предпринять:
- агрегация css и js-скриптов;
- сжатие контента gzip средствами сервера, а не cms;
- оптимизация размера изображений;
- периодическая дефрагментация таблиц базы данных;
- чистка базы от мусора, часто бывает в таблицах blocks и variable как результат неполного или некорректного удаления модулей и тем.

В качестве дополнительных улучшений можно посоветовать следующее:
1) модуль locale_static позволяет сделать локализацию в виде сериализованного массива данных, который еще и прекрасно кэшируется.
2) вместо стандартного поиска можно использовать более производительный движок sphinx, используемый модуль advanced_sphinx.

приветствую! Есть вопросец :)
Я вот тоже установил xcache, и все бы ок, но есть одна проблема!
Работает сервер с Апачем с mod-fcgid, и вопреки настройкам кешера, кеш сбрасывается примерно каждые два часа, (насколько я понимаю, это происходит потому, что fcgi имеет свойство периодически убивать процессы)… И это очень пичально :(
Решается ли как-то этот вопрос с использованием php-FPM, или какими-то другими способами?

Аватар пользователя isn

Можно попробовать поиграться со временем жизни кэша в настройках xcache.