vgrish 24 августа 2013 0 40
Суть идеи такова: сохранить ресурс в виде html странички в кеш memcache средставами MODX (плагином), посредством nginx прочитать ресурс из кеша, так сказать минуя MODX…
Плюсы — nginx отдает страничку практически мгновенно, сервер не тратит ресурсы…

нам нужно два события — OnSiteRefresh и OnBeforeSaveWebPageCache. По первому событию чистить кеш, по второму сохранить ресурс в кеш.
скелет плагина:
switch ($modx->event->name) {
    case 'OnSiteRefresh':
        //чистим кеш
        break;
    case 'OnBeforeSaveWebPageCache':
        if ($modx->resource->get('cacheable') && $modx->resource->get('published') && $modx->resource->_output != '') {
        
            $memcache = new Memcache;
            $memcache->connect('localhost', 11211);
            $expire = 50;//время хранения 50с
            $uri = $modx->resource->get('uri'); 
            $context = $modx->context->get('key');
            $key = "$context/$uri";
            
            $output = &$modx->resource->_output;
            $time = time();
            $cacheArray = array('output'=>$output, 'time'=>$time);
            $tmp_object = $output;
            $memcache->set($key, $tmp_object, false, $expire);
            $memcache->close();

        break;
        }
}

запись в кеш
$memcache = new Memcache;
$memcache->connect('localhost', 11211);//подключаемся к memcache
$expire = 50;//время хранения 5с кеша
$tmp = "<h1>привет</h1>";
$memcache->set('key', $tmp, false, $expire);//записываем по ключу значение в кеш
$memcache->close();//закрываем подключение

получить значение (страничку) из кеша
пока на время проб, просто снипетом
$memcache = new Memcache;
$memcache->connect('localhost', 11211) or die ("Не могу подключиться");
$get_result = $memcache->get('key');
echo "Данные из кеша:".$get_result."
\n";
$memcache->close();


ключ формировать по следующему шаблону:
site_name:context:uri
время хранения кеша пока не знаю, возможно алгоритм какой нибудь придумать, пока можно выставить ручками

p.s. хотел донести до вас основную мысль, интересно выслушать ваше мнение… я не программист и возможно это вообще бредовая идея… Жду ваши коментарии…
40 комментариев
vgrish1
vgrish 25 августа 2013г в 10:10 #
ну и вот первый вопрос )
как получить страничку в html после обработки парсером всех чанков и других тегов… и затем сохранить ее в переменную?
vgrish1
vgrish 25 августа 2013г в 11:45 #
$output = &$modx->resource->_output;
$time = time();
$cacheArray = array('output'=>$output, 'time'=>$time);
$tmp_object = $output;
vgrish1
vgrish 25 августа 2013г в 13:04 #
не чет не то…
vgrish1
vgrish 25 августа 2013г в 11:52 #
да и насчет OnBeforeSaveWebPageCache неправильно думаю, оно срабатывает только когда ресурс сохраняем… какое событие сюда лучше подойдет?
Fi1osof1
Fi1osof 25 августа 2013г в 13:16 #
А что тебя не устраивает? Наоборот правильное событие. Если документ кешируемый и не был еще закеширован, тогда этот плагин и сработает. А если уже закеширован, то у тебя nginx будет отдавать страницу и все.
vgrish1
vgrish 25 августа 2013г в 13:40 #
ну событие сработает один раз когда документ сохраняем, потом из memcache кеш уйдет и тю тю… кеша нет
vgrish1
vgrish 25 августа 2013г в 14:04 #
наверно лучше на OnWebPagePrerender — тогда кеш умер, страничку обновили и кеш снова есть
Fi1osof1
Fi1osof 25 августа 2013г в 14:11 #
Тоже логично. Хотя можно и мемкешу, и MODX-у задать одинаковое время жизни кеша. Тогда будет нормуль.
Fi1osof1
Fi1osof 25 августа 2013г в 14:45 #
В таком случае наверно лучше использовать OnWebPageComplete. Это самое последнее событие, и как видишь, без учета закеширован или нет.
public function _postProcess() {
    if ($this->resourceGenerated && $this->getOption('cache_resource', null, true)) {
        if (is_object($this->resource) && $this->resource instanceof modResource && $this->resource->get('id') && $this->resource->get('cacheable')) {
            $this->invokeEvent('OnBeforeSaveWebPageCache');
            $this->cacheManager->generateResource($this->resource);
        }
    }
    $this->invokeEvent('OnWebPageComplete');
}


Черновой код плагина:
<?php
switch($modx->event->name){
    case 'OnWebPageComplete':
        $key = "resource/".$modx->resourceIdentifier; 
        $output = & $modx->resource->_output;
        $modx->cacheManager->set($key, $output);
        break;
        
    default:;
}


Но сейчас еще буду проверять насколько этот кеш годится. Ведь там php return. Скорее всего надо будет переопределять кеш-провайдер, чтобы сохранять чистый HTML.
vgrish1
vgrish 25 августа 2013г в 14:55 #
так не пойдет, ты стандартно его засунул… тогда можно было вообще плагин не писать, а просто обработчик переключить на cache.xPDOMemCached, и уже nginx выбирать что modx стандартно кеширует!
vgrish1
vgrish 25 августа 2013г в 14:58 #
переопределять? я смутно представляю что это… но я думаю логика такая — обработчик менять на файловый, получать страничку и запихивать уже в memcache
Fi1osof1
Fi1osof 25 августа 2013г в 15:22 #
обработчик менять на файловый, получать страничку и запихивать уже в memcache
Нафига такой изврат? Зачем данные туда-сюда гонять?

Переопределять — это создать свой кеш-провайдер, расширяющий базовый, и туда добавить, к примеру, метод setPureCode() то есть сохранять как есть, а не совать в return;
vgrish1
vgrish 25 августа 2013г в 18:26 #
Ты сможешь это сделать?
Fi1osof1
Fi1osof 25 августа 2013г в 18:33 #
Судя по всему, ничего переопределять не понадобится. Я уже результат получил на уровне нгинкса. Сейчас последние штрихи в плагине делаю. Больше с правилами для нгинкса провозился, я же не сисадмин.
Fi1osof1
Fi1osof 25 августа 2013г в 22:44 #
Ну все, я получил результат конечный. Было несколько подводных камней, так что оформлю в новый топик. Сегодня опубликую, но чуть позже.
vgrish1
vgrish 25 августа 2013г в 22:58 #
Ок!) С нетерпением жду…
Молодчик!
Fi1osof1
Fi1osof 25 августа 2013г в 23:26 #
Пока топик пишу, погоняй: evropa-clinic.modxdev.webtm.ru
Смотри в файрбаге время отклика при повторном заходе.
vgrish1
vgrish 25 августа 2013г в 23:32 #
ну нормал 100 мс...)))
ты только время кеша маленькое выставил… или еще делаешь что то…
как интерпрайз летает)))
Fi1osof1
Fi1osof 25 августа 2013г в 23:43 #
ну нормал 100 мс...)))
На самом деле меньше. У меня 70+- несколько мсек показывает. Из них минимум 40 — это пинг :-)
ты только время кеша маленькое выставил… или еще делаешь что то…
Вообще-то когда ты смотрел, лоэдимпакт в 50 клиентов фигачил по сайту)))
vgrish1
vgrish 25 августа 2013г в 23:59 #
кинь тест глянуть. Нагрузку не мониторил в это время?
Fi1osof1
Fi1osof 26 августа 2013г в 00:13 #
Тест не сохранил, так как бесплатный всегда хрень показывает. Показывал, что секунду с лишним ответ. Нагрузке на сервере вообще не ощущал.

Вот статья: modxclub.ru/blog/research/210.html

А я пошел спать. Удачи
Fi1osof1
Fi1osof 25 августа 2013г в 15:26 #
и уже nginx выбирать что modx стандартно кеширует!
1. Стандартный кеш документа нафиг не нужен, потому что там много хлама.
2. Нгинкс не обрабатывает php.
vgrish1
vgrish 25 августа 2013г в 18:24 #
ну все что ты сказал правильно, и я это вполне понимаю) Но вот выразился так кривовато, а как расширить и т.д. это вообще темный лес…
Непростая задача наверное
Fi1osof1
Fi1osof 26 августа 2013г в 00:14 #
Кстати, стандартным $modx->cacheManager-ом кеш сохранял. Само собой мемовским. И все ОК, ничего лишнего не понадобилось делать.
Fi1osof1
Fi1osof 25 августа 2013г в 13:13 #
А зачем отдельная инициализация мемкеша? Он же в составе MODX-а идет. По умолчанию в настройках MODX-а указано cache_handler=xPDOFileCache, то есть используется файловый кеш-провайдер. Поменяй на cache.xPDOMemCached и все. Кеширование будет на мемкеше. И в плагине спокойно сможешь использовать $modx->cacheManager->set($key, $value); Плюс к этому при сбросе кеша сайта MODX и твой пользовательский кеш будет очищать, не надо ничего дописывать.
vgrish1
vgrish 25 августа 2013г в 13:38 #
да я пока на файловый перешел, чтоб только плагин мне тут мусорил…
Fi1osof1
Fi1osof 25 августа 2013г в 13:49 #
Поменяй на xPDOMemCached и все.
Поправочка — 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",
);

Вот тогда он сразу будет мемкешед юзать.
vgrish1
vgrish 25 августа 2013г в 14:22 #
значит как почистить чеш можно не волноваться, при OnSiteRefresh само все почистится, это уже хорошо)
Fi1osof1
Fi1osof 25 августа 2013г в 13:16 #
Пожалуй, я тоже сейчас попробую это сделать, давно хотел.
Fi1osof1
Fi1osof 25 августа 2013г в 13:22 #
Кстати, имей ввиду, что еще в мемкеш можно объекты сохранять. Только там есть тонкости. Нельзя туда сохранить, к примеру, дескриптор соединения с базой данных. Есть в общем ООП-шные методы __sleep() и __wakeup(). __sleep() сработает прям перед сохранением (автоматически). В нем можно будет удалить переменные $modx и т.п., а __wakeup() сработает автоматически, когда будешь получать объект из кеша (в этот момент ему можно будет опять присвоить $modx). Тоже может пригодиться для тюннинга.
Fi1osof1
Fi1osof 25 августа 2013г в 13:40 #
Кстати, ты memcache ведь используешь? А почему не memcached?
Вот сравнительная статья.
vgrish1
vgrish 25 августа 2013г в 13:56 #
незнаю, изначально как то только memcache на сервер ставил…
Fi1osof1
Fi1osof 25 августа 2013г в 14:09 #
Ставь memcached. Не сложнее устанавливается. Конфиги его будут в /etc/memcached.conf (это если лимит памяти захочешь поднять).
vgrish1
vgrish 25 августа 2013г в 14:13 #
так ты мне обьясни в чем разница то? я вот пока не вижу разницы, так зачем на него переходить.
что cache.xPDOMemCached, что cache.xPDOMemCache — по сути одно и тоже, разъве нет?
Fi1osof1
Fi1osof 25 августа 2013г в 14:22 #
Я же тебе выше дал ссылку на сравнительную статью. Давай я пересказом не буду заниматься, а ты прочитаешь и сам для себя решишь в чем разница и надо или не надо.
vgrish1
vgrish 25 августа 2013г в 14:31 #
ну я для себя сделал выводы что разницы никакой)
Да и статья устарела, может сейчас что то и изменилось… позже покопаю…
В данном вопросе это не столь важно.
vgrish1
vgrish 25 августа 2013г в 14:14 #
как обработанную страничку в кеш запихать в виде html? у меня что то пока не выходит…
Fi1osof1
Fi1osof 25 августа 2013г в 14:23 #
Щас сделаю и скажу.
vgrish1
vgrish 29 августа 2013г в 22:19 #
Наверно надо пересмотреть формирование ключа. Много ограничений накладывается, в md5 нужно кодировать…
vgrish1
vgrish 30 августа 2013г в 09:43 #
в плагине добавляем
$key = md5($key);

в конфиге nginx добавляем в секцию http
perl_set $md5_uri 'sub { 
      use Digest::MD5 qw(md5_hex); 
        my $r = shift;  
        my $args=$r->args; 
        return md5_hex($args); 
     }';

в целом конфиг nginx в таком виде:
user  nginx;
worker_processes  1;
worker_rlimit_nofile 100000;

error_log   /var/log/nginx/error.log;
#error_log  /var/log/nginx/error.log  notice;
#error_log  /var/log/nginx/error.log  info;

pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
    use epoll;
}


http {

    perl_set <strong>$md5_uri</strong> 'sub { 
      use Digest::MD5 qw(md5_hex); 
        my $r = shift; 
        my $uri=$r->uri; 
        my $args=$r->args; 
        return md5_hex($args); 
     }';
    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;
    client_max_body_size 100m;
    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;
    server_tokens   off;
    gzip            on;
    gzip_static     on;
    gzip_comp_level 5;
    gzip_min_length 1024;
    keepalive_timeout  65;
    gzip_proxied        any;
    gzip_types          text/plain text/html text/xml application/xml application/x-javascript text/javascript text/css text/json;
    gzip_disable        "msie6";
    charset             utf-8;
    limit_conn_zone   $binary_remote_addr  zone=addr:10m;

    # Load config files from the /etc/nginx/conf.d directory
    include             /etc/nginx/conf.d/*.conf;
    include             /etc/nginx/sites-enabled/*;


}


ну и конфиг на сайт

upstream backend-test {server unix:/var/run/php5-test.sock;}
server {
    listen              80;
    server_name         test.ru;
    root                /var/www/test/www;
    access_log          /var/log/nginx/test-access.log;
    error_log           /var/log/nginx/test-error.log;
    index               index.php index.html;
    rewrite_log         on;
    if ($host != 'test.ru' ) {
        rewrite  ^/(.*)$  http://test.ru/$1  permanent;
    }
    location ~* ^/core/ {
        deny all;
    }
    location / {
        try_files       $uri $uri/ @rewrite;
    }
    location @rewrite {
        rewrite         ^/(.*)$ /index.php?q=$1;
    }
    
      location ~ \.php$
    {
        default_type            text/html;
        set  $memcached_key       "default/test_<strong>$md5_uri</strong>";
        # где test_ это cache_prefix
        memcached_pass unix:///var/run/memcached/mem.socket;
        error_page     404 401 405 502 504 = @php;
    } 

    location @php {

        include         fastcgi_params;
        fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass    backend-test;
    }
    location ~* ^.+\.(jpg|jpeg|gif|css|png|js|ico|bmp)$ {
       access_log       off;
       expires          10d;
       break;
    }
    location ~ /\.ht {
        deny            all;
    }
}


теперь плагин скидывает в кеш по ключу md5 — соответственно мы уходим от ограничения на длину ключа в 250 символов и по идее теперь не должно быть проблем с кириллицей в uri…
p.s. должно работать, время будет проверю
Авторизуйтесь или зарегистрируйтесь (можно через соцсети ), чтобы оставлять комментарии.