Кэширование с Varnish + ESI
Если на Вашем сайте практически нет динамики, то Вы легко можете складывать все его страницы в кэш и практически не делать запросов к бекенду. Но что делать если на сайте есть персонализированные данные (авторизация, блок пользователя, баннера)?
ESI
ESI позволяет разбить страницу на логические части, а при обработке страницы делать дополнительные запросы для получения содержимого этих частей. Все выглядит довольно просто:
Всем привет!
Всем пока!!!
Web-сервер, поддерживающий ESI вызовы, просто сделает дополнительный запрос, а результат вставит на место ESI инструкции. Допустим наш скрипт “news.php” содержит такой код:
echo "
-
- News title 1
</ul>”;
После обработки первого примера, Web сервер вернет клиенту такую страницу:
Всем привет!
-
-
- News title 1
-
Всем пока!!!
ESI
Запросы ESI можно кэшировать. Следовательно, у Вас появляется удобное средство работы с динамическим контентом. Достаточно разделить их на разные блоки и закэшировать те, которые не изменяются (либо изменяются редко).
[ad]
Как все работает?
Web сервер, который поддерживает ESI, делает запрос к бекенду (в нашем случае, PHP). Далее получив страницу, обрабатывает все ESI вызовы, делая на каждый из них дополнительный запрос для получения содержимого. Далее складывает (или не складывает) все это в кэш и генерирует страницу. Для последующих вызовов Web сервер будет получать данные из кэша и не делать дополнительных ESI запросов.
Порядок подключения ESI
-
-
- Сначала нужно определить блоки на странице, вынести их в отдельные скрипты (каждый блок должен иметь свой адрес)
-
-
-
- Вставить ESI инструкции на месте вынесенных блоков
-
-
-
- Включить кэширование ESI блоков на Web сервере
-
Персонализированные блоки
Самая сложная задача – это кэширование блоков, которые уникальны для каждого пользователя. Например, блок с персональными ссылками на профиль, настройки и превью фотки. В это случае, блок придется сохранять в кэш для каждого пользователя.
Стоит учитывать, что количество таких блоков в кэше будет пропорционально количеству пользователей.
общее количество = количество пользователей х количество блоков на страницу
Подробный пример
Пусть у нас есть сайт с новостями. Новости обновляются каждый час. На сайте также есть блок авторизации и ссылки для авторизированных пользователей. Выделяем такие блоки для ESI:
-
-
- Блок авторизации
-
-
-
- Меню
-
-
-
- Блок новостей
-
Стартовая страница
Тестируем ESI
Новости
## Все скрипты для ESI вызовов будут находиться в папке app
Скрипт авторизации
<? if ( $user ) { ?>
Привет, <? } else { ?>
<? } ?>
Скрипт меню
<? session_start(); ?>
-
- <? if ( $_SESSION[‘user’] ) { ?>
- Пункт меню только для пользователей
<? } ?>
Скрипт новостей
$rss = file_get_contents(‘http://feeds.nytimes.com/nyt/rss/HomePage’);
$xml = simplexml_load_string($rss);
echo ”
-
- “;
-
- foreach ( $xml->channel->item as $item )
-
- {
- echo ”
- “{$item-“>link}” {$item->title}”;
}
echo “
“;
Настройка Web сервера
Для приложения будем использовать Nginx, Varnish будет направлять запросы ему (8090 порт):
server {
listen 8090;
# Если включен gzip, обязательно нужно выключить!
gzip off;
location / {
index index.php;
}
location ~* .(php)$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /home/golotyuk/www/localhost/esi/$fastcgi_script_name;
}
}
Varnish + ESI
Давайте настроим кэш по таким правилам:
-
- Главную страницу кэшируем на 24 часа, блоки – на 1 час
-
- Кэшировать будем все запросы кроме POST
-
- Для кэширования персонального блока будем использовать значение сессионных Cookies (PHPSESSID) – для установки ключа
-
- Для отделения персонализированных блоков от обычных блоков для авторизированных пользователей будем использовать соотв. приставки к запросам: UID (персонализированные блоки) и AUTH (обычные блоки, учитывающие только статус пользователя)
Конфигурация:
backend default { .host = “127.0.0.1”; .port = “8090”; }
# Процедура формирования ключа для кэша
sub vcl_hash {
# Стандартные параметры – имя сервера и URL
set req.hash += req.url;
set req.hash += req.http.host;
# Если установлена сессионная кука, сохраняем ее значение в переменную
if( req.http.cookie ~ “PHPSESSID” ) {
set req.http.X-Varnish-Hashed-On =
regsub( req.http.cookie, “^.*?PHPSESSID=([^;]*?);*.*$”, “1” );
}
# Если в строке запроса мы находим “UID”, то необходимо добавить
# значение сессии в параметры кэширования
if( req.url ~ “/app/.*UID” && req.http.X-Varnish-Hashed-On ) {
set req.hash += req.http.X-Varnish-Hashed-On;
}
# Если в строке запроса мы находим “AUTH”, то необходимо добавить
# флаг статуса (logged in) в параметры кэширования
if( req.url ~ “/app/.*AUTH” && req.http.X-Varnish-Hashed-On ) {
set req.hash += “logged in”;
}
hash;
}
sub vcl_recv {
# Если тип запрос не POST, то ищем объект в кэше
if ( req.request != “POST” )
{
lookup;
}
}
sub vcl_fetch {
# Для запроса “/” используем обработку esi и кэшируем на 1 сутки
if (req.url == “/”) {
esi;
set obj.ttl = 24h;
}
# Для запросов “/app” (ESI вызовы) кэшируем результат на 1 час
elseif (req.url ~ “^/app/”) {
set obj.ttl = 1h;
}
deliver;
}
После проверки скорости получим такие результаты:
ab -n 100 -c 5 http://127.0.0.1/
Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.1 0 0 Processing: 2 5 3.8 3 18 Waiting: 2 5 3.8 3 18 Total: 2 5 3.8 4 19 Percentage of the requests served within a certain time (ms) 50% 4 66% 4 75% 5 80% 8 90% 12 95% 15 98% 17 99% 19 100% 19 (longest request)
На аналогичном скрипте без ESI, который содержит всю туже логику внутри и каждый раз вызывает PHP:
ab -n 100 -c 5 http://127.0.0.1:8090/index_standard.php
Результаты:
Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 Processing: 354 579 666.2 458 5484 Waiting: 354 579 666.2 458 5483 Total: 354 579 666.2 458 5484 Percentage of the requests served within a certain time (ms) 50% 458 66% 492 75% 517 80% 539 90% 602 95% 667 98% 3572 99% 5484 100% 5484 (longest request)
Как видим скорость работы отличается в 100 раз!
Самое важное
ESI позволяет использовать кэширование на динамических сайтах с высокой персонализацией. Обратите внимание на альтернативу – SSI в связке с Nginx.
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: