Pull to refresh

Кешируем блоки HTML при помощи nginx

Reading time 3 min
Views 7K
Не секрет, что пользователи любят, когда контент на сайте обновляется чаще, чем раз в год. Эту любовь пользователей к динамическим страничкам разделяют и поисковики. Google, например, умеет определять наличие обновляющихся блоков на страничке и добавляет ей немного кармы (читай, PR).

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



Простой пример — предположим у вас был замечательный статический HTML, который отдавался nginx-ом со скоростью 2000 раз в секунду (это достижимо без особых проблем). И тут добрый дизайнер нарисовал новый вариант странички, где для залогиненных пользователей присутствует мааааленький блок, содержащий логин, имя-отчество, и, например, аватарку.

Всё, приехали. Статикой тут уже не обойдёшься, а это означает запуск PHP (Perl/Python) на каждый запрос, проверка сессии, ползание по файловой системе (или, ещё хуже, БД) для того, чтобы найти по SID логин и другую инфу о пользователе. Производительность проседает в десять раз. Или нет. :)

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

Фича номер раз, которая в нём есть с незапамятных времён — это SSI. Для того, чтобы её использовать, делаем небольшой скрипт, который умеет принимать из куки идентификатор сессии, и отдавать только тот самый небольшой блок с информацией о пользователе. То есть, на запрос вида GET /get_user_info.php отдаёт фрагмент HTML, вида <div class=«login»>Здравствуйте, Василий Пупкин</div>.

Соответственно, в самой страничке пишем SSI-include: <!--# include virtual="/get_user_info/" -->.

Для того, чтобы эта конструкция заработала, в конфиг-файле nginx надо описать соответствующий location:

location /get_user_info/ {
proxy_pass 127.0.0.1:12345/get_user_info.php;
proxy_pass_header Cookie; # для передачи sid в Cookie
}


Однако, как вы, наверное, уже заметили, подобная конструкция не решает проблемы с производительностью кода, а лишь позволяет вынести динамическую часть в отдельный скрипт. Окей, как раз для этого нужна фича номер два — кеширование прокси-запросов. В production-ветке она появилась сравнительно недавно, но уже позволяет делать очень многое. :)

Итак, для начала, скажем nginx, где мы хотим хранить наш кеш.
proxy_cache_path /var/nginx/cache
levels=1:2 keys_zone=my_cache:64m max_size=1024m
inactive=1d;


Эта строчка означает, что мы создаём новую зону для кеша, с названием my_cache, файлы кеша будут лежать в /var/nginx/cache, занимая не более чем 1 гигабайт, и храниться не более чем 1 день.

Теперь скажем nginx, что мы хотим кешировать запросы к нашей локации.

location /get_user_info/ {
proxy_pass 127.0.0.1:12345/get_user_info.php;
proxy_pass_header Cookie; # для передачи sid в Cookie

proxy_cache my_cache;
proxy_cache_valid 200 3h; # раз в три часа будем позволять себе обновлять кеш :)
proxy_cache_valid any 0; # не кешируем 500 и 400 ошибки
proxy_cache_use_stale updating error timeout invalid_header http_500 http_502 http_503 http_504 http_404; # если ваш скрипт отдал одну из описанных ошибок, использовать вариант из кеша, если он есть, даже если он уже заэкспайрился
proxy_cache_key "$scheme$proxy_host$uri$is_args$args$cookie_sid";
}


Обратите внимание на последнюю строчку. Именно она позволит нам отдавать каждому отдельному пользователю правильный вариант закешированного блока. Точнее, важен её последний параметр — переменная $cookie_sid, значение которой всегда будет равно значению куки с именем «sid», которую вам должен отдать пользователь.

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

Всех, кого интересуют подробности, отправляю на сайт автора nginx за документацией: sysoev.ru/nginx/docs.

UPD: Разобрался с настройками, перенёс в блог nginx.
Tags:
Hubs:
+79
Comments 60
Comments Comments 60

Articles