MODX Revolution + nginx + memcached.

В продолжение темы. Очень кратко опишу свой опыт с экспериментом сохранения кода страницы в memcached, который в дальнейшем уже отдает nginx без лишних запросов, получая код непосредственно из memcached.

Общая идея: генерировать страницу средствами MODX-а и сохранять ее код полностью в memcached, чтобы в дальнейшем nginx проверял наличие кода страницы в memcached-е, и если есть, отдавал код сразу, не отправляя запрос на php. Пока что это только для сайтов-визиток и т.п. (так как на таких страницах нельзя использовать логику с учетом авторизации пользователей и т.п.), но в дальнейшем планирую страницы делать полностью статическими, а всю динамику переносить на Javascript + AJAX.

Основные проблемы, с которыми столкнулся:

  1. Больше всего времени потратил на настройку правил для nginx-а (свой вариант покажу ниже).
  2. Битая кодировка кеша больших страниц. Как выяснилось, в php для memcached стояла настройка по умолчанию memcached.compression_threshold=2000. То есть если кешируемая строка больше 1999 символов, то php компрессирует ее. Если получить кеш средствами php, то проблем нет, он и декомпрессирует. А вот nginx за этим не следит и отдает как есть. Полечил топорно: в настройках php указал memcached.compression_threshold=9999999

Итак, код плагина:

<?php
switch($modx->event->name){
case 'OnWebPageComplete': 
if (!empty($modx->resource) 
&& $modx->resource->get('cacheable') 
&& $modx->resource->get('published') 
&& $modx->resource->_output != ''
)

{
$key = '/index.php?';

// Заменяем спецсимвол амперсанта
$key .= str_replace('&', '&',($_SERVER['QUERY_STRING']));

// $modx->log(1, "Key: '{$key}'");

$modx->cacheManager->set($key , $modx->resource->_output);
}
break;;

default:;
}

Да, плагин вешаем на событие OnWebPageComplete. Здесь мы этот момент обсуждали.

Настройка для nginx:

server {
listen 80;
# Указываем свой хост
server_name myhost; 
allow all; 

error_log /var/log/nginx/error_log;

# Указываем путь до директории сайта
root /var/www/site/public_html; 

# Всякие запрещенные папки
location ~ /\.ht {
deny all;
}
location /core/ {
deny all;
}
location /temp/ {
deny all;
}

# Статика	
location ~* \.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|tar|wav|bmp|rtf|js)$ { 
access_log off;
expires 30d;
}	

# Основное правило обработки запросов	
location / {
# файлы по умолчанию
index index.php index.html;

# попытка получить запрашиваемый файл в именнованной локации
# Попытка - подразумевает возможные неудачи. Перечисление по порядку 
# в указанных вариантах. 
# При запросе к корню сайта будет получен индексный файл index.php,
# так что сработает правило location ~ \.php$
# А правило @modx сработает только если файл не найден
try_files $uri $uri/ @modx;
}

# Именнованная лакация (правило)
location @modx { 
# выполняем подмену на index.php
rewrite ^/(.*)$ /index.php?q=$1 last;
} 

# Правила обработки php
location ~ \.php$
{
# Контент по умолчанию, чтобы нгинкс не отдавал браузеру
# как обычный файл для скачивания
default_type text/html;

# устанавливаем ключ-переменную для запроса к мемкешу
set $memcached_key "default/$uri?$args";

# выполняем запрос к мемкешу
memcached_pass localhost:11211;

# устанавливаем обработчик ошибок
# если будет получена одна из этих ошибок, то ошибка не будет 
# возвращена сразу, а будет отправлен запрос дальше на php
error_page 404 401 405 502 504 = @php;
} 

location @php { 
# Отправляем запрос на Apache или типа того (у кого что)
proxy_pass http://127.0.0.1:8888; # свой локальный порт
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_connect_timeout 120;
proxy_send_timeout 120;
proxy_read_timeout 180;
} 
}

Напоследок хочу отметить, что в запросах учитываются все GET-параметры, так что этот метод годится и для постраничности и т.п. Любое изменение параметров запроса — это новая страница, новый кеш.

UPD: Забыл сказать, что в MODX-е еще надо сменить кеш-провайдер на memcached. По умолчанию в настройках MODX-а указано cache_handler=xPDOFileCache, то есть используется файловый кеш-провайдер. Надо поменять на cache.xPDOMemCached.

И еще момент: чтобы сразу есть настройки, которые необходимо и в сам конфиг-файл переносить. Дело в том, что для чтения системных настроек из базы данных требуется инициализация самого MODX-а. Это звучит очень логично, но не все на этом внимание заостряют. Так вот, инициализация требует предварительного чтения конфигов. И вот что получается — пока MODX не получил данные из базы данных, он использует конфиги файловые, в том числе и значения по умолчанию. А так как настройка cache_handler=cache.xPDOMemCached хранится в базе данных, то в момент инициализации MODX еще ничего о ней не знает, и соответственно использует стандартный файловый кеш-провайдер. И получив настройки из БД, он только потом очухивается, и начинает использовать Memcached. Но до этого успевает записать конфиги в файлы, и вообще каждый раз при старте читает конфиги из файлов.

Вот чтобы этого не происходило и чтобы он сразу использовал Memcached, зайди в core/config/config.inc.php и в $config_options пропиши эту настройку тоже:

$config_options = array (
"cache_handler" => "cache.xPDOMemCached",
);

Вот тогда он сразу будет мемкешед юзать.