---
title: "Кеширование блоков с помощью nginx"
description: "Многим разработчикам знакома ситуация когда кешировать страницы сайта, скажем, на 5-10 минут нельзя ..."
author: "Obolganliquida"
published: "2010-08-16T09:10:08+00:00"
modified: "2010-08-16T09:10:08+00:00"
locale: "ru"
canonical_url: "https://yvision.kz/post/keshirovanie-blokov-s-pomoshchyu-nginx-65769"
markdown_url: "https://yvision.kz/post/keshirovanie-blokov-s-pomoshchyu-nginx-65769/markdown"
site_name: "Yvision.kz"
---

# Кеширование блоков с помощью nginx

> Многим разработчикам знакома ситуация когда кешировать страницы сайта, скажем, на 5-10 минут нельзя ...

[!\[nginx + SSI\](http://linux.ria.ua/img/articles/SsiBlocks/nginx_ssi.gif)](http://linux.ria.ua/SsiBlocks/src/bin/index.php)Многим разработчикам знакома ситуация когда кешировать страницы сайта, скажем, на 5-10 минут нельзя всего из-за одного небольшого блочка, актуальность которого нужно поддерживать если не в реальном времени, то с временем «старения» не больше 5-10 секунд. При этом посещаемость сайта продолжает расти, растет время генерации страниц и c этим надо что-то делать…
- **Вариант решения 1:** *Подкрутить то, до чего не доходили руки последнее полгода. Все Вас поймут и передвинут сроки на другие задачи. Вы будете в роли «Супермена» один спасать сайт от непомерной нагрузки, решая проблему «бесплатно» (без доп. вливаний в оборудование). Вам может пригодиться статья [«Тюнинг nginx»](http://habrahabr.ru/blogs/nginx/56497/)*.

- **Вариант решения 2:** *Улучшить техническую базу (докупить мозгов на сервер, улучшить дисковую систему, поставить под БД отдельный сервер). В принципе проблема не решена, а скорее отложена. Теперь у Вас есть время «окопаться» и подготовиться ко второй волне наплыва нагрузки, она будет больше и накроет сильнее.*
- **Вариант решения 3:** *Ваш вариант, о котором я, вероятно, узнаю из комментариев.*

Позвольте предложить и мне проверенное и относительно простое решение на базе одной из старейших технологий в Web-разработке.

## Как это должно работать

Cайт всегда можно разбить на некоторое число независимых блоков, генерацией которых может заниматься (при необходимости) разные сервера. При этом сборкой блоков в единое целое занимается некий «сборщик» и если любой из блоков по какой-то причине не создан за отведенное ему время, то это еще не повод выдавать клиенту «Gateway timeout» или «Internal Server Error». Можно собрать успешно созданные блоки, а на месте «сбойных» показывать устаревший контент из кеша. Для реализации такой модели нам понадобиться технология-ветеран Web-разработки: [ssi](http://sysoev.ru/nginx/docs/http/ngx_http_ssi_module.html). В качестве «сборщика», как ясно из названия статьи, выступает [nginx](http://sysoev.ru/nginx/). «Чудеса» станут возможны благодаря модулю [fastcgi_cache](http://sysoev.ru/nginx/docs/http/ngx_http_fastcgi_module.html). Итак, поехали:

## Исключаем лишнее звено

Нам не пригодиться apache, наличие которого, как правило объясняется использованием RewriteRules. [В nginx есть аналог mod_rewrite](http://sysoev.ru/nginx/docs/http/ngx_http_rewrite_module.html) или [комбинация location/alias с регулярными выражениями](http://sysoev.ru/nginx/docs/http/ngx_http_core_module.html#alias), возможности которых позволяют написать аналог любому RewriteRule от apache. Кроме того в современных фреймворках разбором входного URL может заниматься сам движек (например [Zend_Controller_Router_Rewrite](http://framework.zend.com/manual/ru/zend.controller.router.html) в [Zend Framework](http://framework.zend.com/)) В качестве fastcgi-бекенда может использоваться любая платформа. Примеры будут на php, но это не означает что нельзя написать аналогичный код на python-е или perl-е. Запускаем php в режиме fastcgi: `# /bin/su -m www_user -c "PHP_FCGI_CHILDREN=8 /usr/bin/php-cgi -q -b 127.0.0.1:7777 &"` Можно еще прописать путь к лог-файлу в php.ini (error_log = /var/log/fastcgi/fastcgi.log), но при этом придется перезагружать php-cgi. Делаем: `# killall php-cgi` и запускаем все по-новой Более продвинутый вариант запуска fastcgi — установка [php-fpm](http://php-fpm.org/).

## Устанавливаем nginx

Можно ставить стандартный из репозитория/портов… Но если хотите чтоб работала возможность «почистить» любой файл в кеше, придется компилировать. Нам понадобиться модуль: [ngx_cache_purge](http://labs.frickle.com/nginx_ngx_cache_purge/) Я подробно опишу, как это можно сделать для redhat-подобной системы, а вы уж по аналогии компилируйте под вашу систему. ```

# cd ~/rpmbuild/SRPMS

# yumdownloader --source nginx

# rpm -ivh nginx-0.7.65-1.fc12.src.rpm ``` редактируем файл nginx.spec, где-нибудь в список **./configure** вставляем строчку**--add-module=/root/rpmbuild/BUILD/ngx_cache_purge-1.0 \**. Тут же можно удалить строчки с ненужными модулями (например --with-ipv6 \, --with-http_dav_module \, --with-mail \, --with-mail_ssl_module \ ...) теперь распаковываем содержимое [http://labs.frickle.com/files/ngx_cache_purge-1.0.tar.gz](http://labs.frickle.com/files/ngx_cache_purge-1.0.tar.gz) в папку **/root/rpmbuild/BUILD/ngx_cache_purge-1.0**. Все можно компилировать: ```

# cd ~/rpmbuild/SRPMS

# rpmbuild -ba nginx.spec ``` Это не совсем красивый способ, т.к. полученный в результате .src.rpm не будет содержать файла с модулем ngx_cache_purge. Если для вас, все же, это критично, то здесь можете [загрузить «правильный» вариант nginx .src.rpm](http://linux.ria.ua/SRPMS/nginx/) для ветки 8.xx. Правда я часть ненужных мне модулей закомментировал. Устанавливаем пересобранный nginx на наш сервер:

# rpm -ivh nginx-0.7.65-1.fc12.x86_64.rpm

## Настройка nginx для проекта на php

В файл **/etc/hosts** (добавляем): ```

# Virtual hosts 127.0.0.1 myproject ``` В основном конфиге **/etc/nginx/nginx.conf** в секцию http добавляем: ``` fastcgi_cache_path /var/spool/nginx/cache levels=1:2 keys_zone=mycache:64m; include /etc/nginx/conf.d/*.conf; ``` (Не забудьте создать папку /var/spool/nginx/cache и установить для нее пользователя, под которым запускается nginx) В папке **/etc/nginx/conf.d**/ создаем конфиги для виртуальных хостов Пример кофига (**/etc/nginx/conf.d/myproject.conf**): ``` server { listen 80; server_name myproject; root /var/www/myproject/public; ssi on;

# Включаем кеш если есть такая необходимость fastcgi_cache mycache; fastcgi_cache_min_uses 1;

# Время кеширования равно нулю. кеш включен но кеширования нет

# Время кеширования для конкретных страниц указиваем в заголовке "Cache-Control" fastcgi_cache_valid 200 0m; fastcgi_cache_valid 404 1m; fastcgi_cache_valid 500 0m; fastcgi_cache_use_stale updating error timeout invalid_header http_500;

# Используем вариант из кеша (даже если он устарел) в случае ошибки fastcgi_cache_key $uri$is_args$args;

# Раскоментируйте эту секцию если nginx собран с модулем ngx_cache_purge

# location ~ ^/purge(/.*) {

# fastcgi_cache_purge mycache $1$is_args$args;

# } location ~ /(img|css|js|assets) {

# access_log off; access_log /var/log/nginx/myproject_img_access.log main; expires 1h; } location / { access_log /var/log/nginx/myproject_main_access.log main; error_log /var/log/nginx/myproject_error.log; fastcgi_pass 127.0.0.1:7777; fastcgi_index index.php; include fastcgi.conf; } } ``` Устанавливаем тестовый проект на php в **/var/www/myproject**. [Исходный код примера можно посмотреть и скачать здесь](http://linux.ria.ua/SsiBlocks/). Запускаем nginx. Для RedHat-подобных систем это выглядит приблизительно так: ```

# service nginx start ``` Все, система готова к работе! Пробуем запустить **http://myproject/**

## Учим backend управлять временем кеширования

Дело в том, что в nginx время кеширования указывается в параметре fastcgi_cache_valid 200 0m; и распространяется на все страницы, в которых заголовком оно не переопределено. В конфиге «по умолчанию» время кеширования я указал равным 0, т.е. кеширование отключено. Но если бекенд сгенерирует заголовок приблизительно такого вида: ``` Cache-Control: public, max-age=20 ``` либо ``` Expires: Thu, 18 Mar 2010 20:57:07 GMT ``` То страница nginx-ом будет закеширована на 20 секунд. В php заголовок можно поменять с помощью функции header() *(Со слов автора nginx самым приоритетным является «X-Accel-Cache-Control», потом «Cache-Control», потом «Expires»)*. Напишем небольшую функцию. котрая будет управлять временем кеширования: ``` function cacheHeaders($lifetime=0) {

# $date = gmdate("D, d M Y H:i:s", time() + $lifetime);

# header('Expires: ' . $date . ' GMT'); header('Cache-Control: public, max-age=' . $lifetime); } ```

## Мастерим блоки

Блоком будем называть любую логически выделенную часть html-кода без стандартных заголовков html-старницы, например: ``` Это простой блок ``` Чтоб визуально контролировать состояние свежести каждого из блоков добавим код, наших тестовых блоков вывод времени. ``` ``` [Смотрим рабочий пример с использованием SSI блоков.](http://linux.ria.ua/SsiBlocks/src/bin/index.php)

## Удаляем страницы из кеша

К сожалению у nginx-а пока что нету родного (штатного) способа удаления страничек из кеша. Иногда это может создавать неудобства. Если вы добавили при компиляции модуль ngx_cache_purge, то в конфиг (/etc/nginx/conf.d/myproject.conf) добавим приблизительно такую секцию, перед секцией «location / {...» : ``` location ~ ^/purge(/.*) { #allow 127.0.0.1; #allow 10.1.1.0/24; #deny all; fastcgi_cache_purge mycache $1$is_args$args; } ``` Для того чтоб удалить закешированную страницу: http://myproject/mypage.php?lang=ru, мне достаточно загрузить страницу http://myproject/purge/mypage.php?lang=ru В php это можно сделать командой file_get_contents(«http://myproject/purge/mypage.php?lang=ru»); С помощью директив **allow** и **deny** можно ограничить круг хостов с которых можно «чистить» кеш.

## Тестируем

Напоминаю, ссылка для тестов [http://linux.ria.ua/SsiBlocks/src/bin/index.php](http://linux.ria.ua/SsiBlocks/src/bin/index.php). Обратите внимание, «каркас» страницы обновляется раз в 10 секунд, остальные блоки обновляются согласно примечаниям под временем создания блока. Самый большой интерес, на мой взгляд, представляет «Збойный блок». Если вы введете его в режим имитации сбоя, вы все равно будете видеть «несбойную» версию этого блока пока не очистите кеш. Кроме того, помните, что вы не одни сейчас проводите эксперименты с этой страничкой, если хотите поэкспериментировать — самостоятельно настройте [локальную копию примера](http://linux.ria.ua/SsiBlocks/download/).

## Делаем выводы

Даже если такой подход покажется Вам примитивным, и функциональность его сильно ограниченной, обратите внимание на то, что это работает не просто быстро, а очень быстро! Узким местом может быть только дисковая система, если кеш «распухнет» до больших размеров и не будет помещаться в дисковый кеш. *PS: Если эта статья будет интересна читателям, я планирую написать вторую часть о применении описанного подхода к кешированию блоков на Zend Framework.*

---

Source: [https://yvision.kz/post/keshirovanie-blokov-s-pomoshchyu-nginx-65769](https://yvision.kz/post/keshirovanie-blokov-s-pomoshchyu-nginx-65769)