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

Date August 28th, 2012 Author Vitaly Agapov

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

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

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

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

Зачин

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

curl http://repo.varnish-cache.org/debian/GPG-key.txt | sudo apt-key add -
echo "deb http://repo.varnish-cache.org/ubuntu/ lucid varnish-3.0" | sudo tee -a /etc/apt/sources.list
sudo apt-get update
sudo apt-get install varnish

Параметры демона можно подкрутить в /etc/default/varnish. А подкручивать на продакшене их надо. При нехватке тредов, например, варниш может перестать отвечать на запросы от апача. У меня стоит так:

NFILES=131072
MEMLOCK=82000
INSTANCE=$(uname -n)
VARNISH_VCL_CONF=/etc/varnish/default.vcl
VARNISH_LISTEN_PORT=6081
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
VARNISH_ADMIN_LISTEN_PORT=6082
VARNISH_MIN_THREADS=1000
VARNISH_MAX_THREADS=20000
VARNISH_THREAD_TIMEOUT=120
VARNISH_STORAGE_FILE=/var/lib/varnish/$INSTANCE/varnish_storage.bin
VARNISH_STORAGE_SIZE=2G
VARNISH_SECRET_FILE=/etc/varnish/secret
VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}"
VARNISH_TTL=120

DAEMON_OPTS="-a :${VARNISH_LISTEN_PORT} \
             -f ${VARNISH_VCL_CONF} \
             -T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \
             -t ${VARNISH_TTL} \
             -w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} \
             -S ${VARNISH_SECRET_FILE} \
             -s ${VARNISH_STORAGE}"

 

Кульминация

Логика работы описывается с помощью языка VCL в файле (по умолчанию) /etc/varnish/default.vcl. В листинге я раскидал кое-какие комментарии, но главный секрет заключается в блоке vcl_hash, задающем, из каких составляющих образуется хэш-ключ для сохранения кэша. Мы с помощью функции hash_data добавляем к хэшу куки, из которых заранее в vcl_recv вырезали все, кроме нужных.

Следует обратить внимание, что в версии 2.х hash_data ещё не было. Тогда надо было набивать хэш командами вида set req.hash += req.url.

# путь к бэкенду
backend web1 {
   .host = "127.0.0.1";
   .port = "8080";
}
# обработка запросов
sub vcl_recv {
   # можно удалить авторизацию и куки. Куки мы пока оставляем
   if (req.http.Authorization) {
      #unset req.http.cookie;
      unset req.http.authorization;
   }
   # удаляем все куки кроме нужных нам needed.cookie1 и needed.cookie2.
   if (req.http.Cookie) {
      set req.http.Cookie = ";"+req.http.Cookie;
      set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
      set req.http.Cookie = regsuball(req.http.Cookie, ";(needed.cookie1|needed.cookie2)=", "; \1=");
      set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
      set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
      if (req.http.Cookie == "") {
         remove req.http.Cookie;
      }
   }
   set req.backend = web1; 
   if (req.request != "GET" && req.request != "HEAD") {
      /* Работаем только с GET и HEAD */
      return (pass);
   }
   return (lookup);
}
sub vcl_hash {
   # Создаём хэш из урла и кук
   hash_data(req.url);
   hash_data(req.http.cookie);
   return (hash);
}
# обработка ответа от бэкенда
sub vcl_fetch {
   # нужный урл кэшируем на 10 секунд, остальные - не кэшируем
   if (req.url ~ "some-path-to-cache") {
      set beresp.ttl = 10s;
   }
   else {
      set beresp.ttl = 0s;
   }
   # удаляем заголовок Set-Cookie из ответа
   remove beresp.http.Set-Cookie;
   return (deliver);
}
# доставка ответа
sub vcl_deliver {
   # удаляем ненужные специфичные заголовки varnish
   unset resp.http.Via;       
   unset resp.http.X-Varnish;
   return (deliver);
}

 

Развязка

Теперь надо спроксировать нужные урлы с 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