Varnish: кэширование с учётом cookies

Date August 28th, 2012 Author Vitaly Agapov

— Думает, думает, – проворчал Муравей. — Что бы стало в лесу, если б все думали.

Сергей Козлов «Ёжик в тумане»

Возникла задача поднять кэширующий прокси-сервер. Но не простой прокси-сервер, а такой, который сможет кэшировать страницы в зависимости от определённой куки, определённым образом влияющей на отображение страницы.

Вариантов решения скорее всего здесь может быть много, но я смотрел в сторону varnish, с которым раньше приходилось работать и который мне чрезвычайно симпатичен. Главным же препятствием казалось то, что varnish по умолчанию не кэширует страницы, содержащие куки – раньше никогда этого и не требовалось, а куки и вовсе вырезались. Но решение нашлось. Varnish не подвёл.

Зачин

Для начала сабж нужно поставить в систему. В Ubuntu, на которой ставились эксперименты, он есть в стандартных репозиториях, но не самой последней свежести. Самой же последней свежести (на момент написание этих строк – 3.0.3) можно поставить из оф.репа:

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:
Category: Varnish, Web-dev | 1 Comment »

Comments

Один комментарий на “Varnish: кэширование с учётом cookies”

  1. Akexander

    polipo?

Leave a comment

 Comment Form