Varnish: кэширование с учётом cookies
Date August 28th, 2012 Author Vitaly Agapov
Возникла задача поднять кэширующий прокси-сервер. Но не простой прокси-сервер, а такой, который сможет кэшировать страницы в зависимости от определённой куки, определённым образом влияющей на отображение страницы.
Вариантов решения скорее всего здесь может быть много, но я смотрел в сторону varnish, с которым раньше приходилось работать и который мне чрезвычайно симпатичен. Главным же препятствием казалось то, что varnish по умолчанию не кэширует страницы, содержащие куки – раньше никогда этого и не требовалось, а куки и вовсе вырезались. Но решение нашлось. Varnish не подвёл.
Зачин
Для начала сабж нужно поставить в систему. В Ubuntu, на которой ставились эксперименты, он есть в стандартных репозиториях, но не самой последней свежести. Самой же последней свежести (на момент написание этих строк – 3.0.3) можно поставить из оф.репа:
1.
curl http://repo.varnish-cache.org/debian/GPG-key.txt |
sudo
apt-key add -
2.
echo
"deb http://repo.varnish-cache.org/ubuntu/ lucid varnish-3.0"
|
sudo
tee
-a /etc/apt/sources.list
3.
sudo
apt-get update
4.
sudo
apt-get
install
varnish
Параметры демона можно подкрутить в /etc/default/varnish. А подкручивать на продакшене их надо. При нехватке тредов, например, варниш может перестать отвечать на запросы от апача. У меня стоит так:
01.
NFILES=131072
02.
MEMLOCK=82000
03.
INSTANCE=$(uname -n)
04.
VARNISH_VCL_CONF=/etc/varnish/default.vcl
05.
VARNISH_LISTEN_PORT=6081
06.
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
07.
VARNISH_ADMIN_LISTEN_PORT=6082
08.
VARNISH_MIN_THREADS=1000
09.
VARNISH_MAX_THREADS=20000
10.
VARNISH_THREAD_TIMEOUT=120
11.
VARNISH_STORAGE_FILE=/var/lib/varnish/$INSTANCE/varnish_storage.bin
12.
VARNISH_STORAGE_SIZE=2G
13.
VARNISH_SECRET_FILE=/etc/varnish/secret
14.
VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}"
15.
VARNISH_TTL=120
16.
17.
DAEMON_OPTS="-a :${VARNISH_LISTEN_PORT} \
18.
-f ${VARNISH_VCL_CONF} \
19.
-T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \
20.
-t ${VARNISH_TTL} \
21.
-w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} \
22.
-S ${VARNISH_SECRET_FILE} \
23.
-s ${VARNISH_STORAGE}"
Кульминация
Логика работы описывается с помощью языка VCL в файле (по умолчанию) /etc/varnish/default.vcl. В листинге я раскидал кое-какие комментарии, но главный секрет заключается в блоке vcl_hash, задающем, из каких составляющих образуется хэш-ключ для сохранения кэша. Мы с помощью функции hash_data добавляем к хэшу куки, из которых заранее в vcl_recv вырезали все, кроме нужных.
Следует обратить внимание, что в версии 2.х hash_data ещё не было. Тогда надо было набивать хэш командами вида set req.hash += req.url.
01.
# путь к бэкенду
02.
backend web1 {
03.
.host = "127.0.0.1";
04.
.port = "8080";
05.
}
06.
# обработка запросов
07.
sub vcl_recv {
08.
# можно удалить авторизацию и куки. Куки мы пока оставляем
09.
if (req.http.Authorization) {
10.
#unset req.http.cookie;
11.
unset req.http.authorization;
12.
}
13.
# удаляем все куки кроме нужных нам needed.cookie1 и needed.cookie2.
14.
if (req.http.Cookie) {
15.
set req.http.Cookie = ";"+req.http.Cookie;
16.
set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
17.
set req.http.Cookie = regsuball(req.http.Cookie, ";(needed.cookie1|needed.cookie2)=", "; \1=");
18.
set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
19.
set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
20.
if (req.http.Cookie == "") {
21.
remove req.http.Cookie;
22.
}
23.
}
24.
set req.backend = web1;
25.
if (req.request != "GET" && req.request != "HEAD") {
26.
/* Работаем только с GET и HEAD */
27.
return (pass);
28.
}
29.
return (lookup);
30.
}
31.
sub vcl_hash {
32.
# Создаём хэш из урла и кук
33.
hash_data(req.url);
34.
hash_data(req.http.cookie);
35.
return (hash);
36.
}
37.
# обработка ответа от бэкенда
38.
sub vcl_fetch {
39.
# нужный урл кэшируем на 10 секунд, остальные - не кэшируем
40.
if (req.url ~ "some-path-to-cache") {
41.
set beresp.ttl = 10s;
42.
}
43.
else {
44.
set beresp.ttl = 0s;
45.
}
46.
# удаляем заголовок Set-Cookie из ответа
47.
remove beresp.http.Set-Cookie;
48.
return (deliver);
49.
}
50.
# доставка ответа
51.
sub vcl_deliver {
52.
# удаляем ненужные специфичные заголовки varnish
53.
unset resp.http.Via;
54.
unset resp.http.X-Varnish;
55.
return (deliver);
56.
}
Развязка
Теперь надо спроксировать нужные урлы с apache на varnish. Для этого можно использовать mod-proxy или mod-rewrite. Вот пример с mod-rewrite:
RewriteRule ^/(.*/some-path-to-cache/.*)$ http://our.varnish.ip:6081/$1 [P]
Как проверить работу varnish? Во-первых, нужно смотреть firebug'ом заголовок Age в ответе от сервера. Там указывается возраст страницы в кэше в секундах. Во-вторых, очень много информации можно получить утилитой varnishstat. Среди прочей информации она показывает:
Hitrate avg: 0.9898 Что из кэша отдаётся 98% запросов.
5137426 Cache hits Сколько отдано запросов из кэша с момента запуска
83205 Cache misses Сколько было запросов к бэкенду
Tags: Varnish
Category:
Varnish, Web-dev |
1 Comment »
21 March 2013 - 11:34
polipo?