Re: proxy cache, cache miss и byte range
c0re
nginx-forum на nginx.us
Сб Ноя 26 00:16:30 UTC 2011
Доброй ночи
При возникновении подобных проблем (а
вы далеко не первый, столкнувшийся с
этой вопиющей несправедливостью),
рекомендации ведущих собаководов =] не
изобилуют разнообразием.
Как правило, во всех подобных случаях,
они являют собой предложение окунуться
с головой в чрезвычайно увлекательное
и полное опасностей приключение,
состоящее из нескольких этапов: 1)
ознакомиться с термином busylock 2)
реализовать его собственными силами
(или подождать преследующих вас
товарищей, добравшихся, также как и вы,
до второго уровня).
Если с первым этапом все, как правило,
гладко, то второй предварительно
требует начальной военной подготовки
(или наличия желания и свободного
времени реализовывать эти busylock'и у
старшего офицерского состава).
Если принять во внимание, что
интересоваться таким поведением nginx'а
начали, мягко говоря, не вчера, ровно
как и получать ответы про busylock'и, то, в
какое кол-во времени может вылиться
занятие выжидательной позиции на
втором этапе, наверное не взялась бы
предсказать даже Ванга, будь она жива.
Тут уже самое время начать грустить, но
не все так безнадежно.
К счастью, проблему можно решить проще
и силами самого nginx'а, НО... с некоторыми
оговорками.
1) С файловой системой работает только 1
nginx ( в смысле файловая система не
расшарена между несколькими серверами,
где на каждом летит свой nginx ).
2) Использовать proxy_store, вместо proxy_cache.
Это ограничивает возможности по
кэшированию. Теоретически возможно
реализовать эту схему и для proxy_cache, но
над этим надо ломать голову отдельно.
Несколько слов об этом в конце.
3) При самом первом запросе файла с
другого сервера, которого еще нет в
локальном кэше у запрашиваемого
сервера, запрещен Range:bytes= запрос.
4) Перерасход трафика между узлами для
горячего контента, если во время
начального скачивания файла в кэш,
будут поступать пареллельные запросы
на скачивание этого же файла. Этот
пункт можно обойти, но не очень
корректно. Например кормить таких
клиентов редиректами, пока файл не
окажется в локальном кэше.
Существенны для вас эти оговорки или
нет, я не знаю, для нас они помехой не
стали, так как мы работаем с обычными
большими файлами и кэширование сложных
запросов нас не интересует.
Вот упрощенный вариант конфига:
http {
...
limit_zone perUri $uri 10m;
limit_zone perIpUri $binary_remote_addr$uri 10m; # не
обязательно
open_file_cache_errors off;
proxy_temp_path /storage/cache-v2/proxy 1 2;
...
}
server {
...
# обращение как к origin'у
# просто отдаем локальные файлы
location /storage/edge-current/ {
alias /storage/local/;
}
# обращение к файлам, для которых мы
не origin
# далее foreach $x in list_of_edges
location /storage/edge-$x/ {
root /storage/cache/edge-$x; # путь к
локальному кэшу
try_files $uri @proxy-edge-$x; # если в локальном
кэше запрашиваемого файла нет, то дуем
в @proxy-edge-$x
}
location @proxy-edge-$x {
limit_conn perUri 1; # только один
запрос к конкретному URI
root /storage/cache/edge-$x; # путь к
локальному кэшу
proxy_set_header Range ""; # запрещаем Range:bytes=
запросы в этой секции. иначе в store
ничего не ляжет
proxy_set_header User-Agent "Edge-$x: proxy"; #
необязательно, для дебага и статистики
proxy_store on;
proxy_buffering on;
proxy_ignore_client_abort on; # если клиент
обрывает соединение, то файл все равно
дотянется и ляжет в локальный кэш
proxy_pass http://edge-$x.domain.com; # проксируем
на origin
error_page 503 = @stream-edge-$x; # если сработал
limit_conn (уже выполняется скачивание
этого файла в локальный кэш),
# дуем в @stream-egde-$x
}
location @stream-edge-$x {
proxy_set_header User-Agent "Edge-$x: stream"; #
необязательно, для дебага и статистики
proxy_buffering off; # прокачиваем файл
через себя напрямую, со скоростью
клиента, proxy_temp_file не создается
# с радостью
разрешаем Range:bytes=, так как чем меньше
траффика, тем лучше
#limit_conn perIpUri 1; # если вы уверены,
что существенная часть ваших
пользователей не сидят за NATом
# и не идут с
одного и того же ip адреса, можно
ограничить кол-во сессий в этой секции
tcp_nodelay on;
proxy_pass http://edge-$x.domain.com; # проксируем
на origin
}
# /foreach
...
}
Для удобства, копия конфига на
http://pastie.org/private/b0bcixwojzgtp6cxiuntxw
Используем nginx 1.0.10
Собственно все просто.
A) Смотрим файл в локальном кэше, если
нашли - то отдаем.
B) Если не нашли, идем качать с origin'а на
максимальной скорости, ложить в кэш и
параллельно отдавать клиенту. HEAD
запросы проксируются напрямую и к
скачиванию файла не приводят.
C) Если во время работы 'B' поступил
запрос на этот же файл, мы просто
проксируем эти запросы напрямую
клиенту в обход буферов и кэшей.
Вы можете задать другое поведение для
секции 'C'. Например кормить клиента
редиректами, пока не освободится
секция 'B' и потом отдать
файл с локального кэша, если клиент
сможет дождаться.
Также можно усложнить конфиг, чтобы
файл качался не сразу с origin'а, а
поискать его сначала в локальных кэшах
на соседних узлах.
В общем, может быть решение и не
идеальное, но пока все работает почти
замечательно, что не может не радовать.
Есть один досадный момент - несмотря на
proxy_ignore_client_abort on, файлы в proxy_temp_files все
таки периодически залипают.
Поэтому приходится заботиться о
периодической чистке протухших
временных файлов: find -amin +60 вполне
справляется.
Может кто-нибудь в состоянии пролить
свет на такое поведение nginx'а? А еще
лучше workaround. Был бы весьма признателен.
Теперь о причине, почему нужно
использовать proxy_store, вместо proxy_cache.
При использовании proxy_store, мы в конфиге
nginx'а имеем возможность проверить,
закэширован ли уже такой файл в
локальном кэше.
Способов проверки силами самого nginx'а
для proxy_cache мне обнаружить не удалось.
Это можно сделать либо внешним
скриптом, либо, может быть, через
скриптовый модуль, например lua.
Если кто-то может подсказать рабочее
решение для определения, опять же буду
весьма благодарен.
proxy_store имеет как преимущества -
например горячий контент можно заранее
разложить по узлам самостоятельно
(хотя и для proxy_cache можно, но сложнее),
так и недостатки - придется
самостоятельно следить за свободным
местом в локальном кэше и чистить
наименее востребованные файлы.
Вот примерно так. Будет здорово, если
это кому-нибудь поможет.
Posted at Nginx Forum: http://forum.nginx.org/read.php?21,219025,219080#msg-219080
Подробная информация о списке рассылки nginx-ru