Welcome! Log In Create A New Profile

Advanced

Re: proxy_cache, cache miss и byte range

November 25, 2011 07:16PM
Доброй ночи

При возникновении подобных проблем (а вы далеко не первый, столкнувшийся с этой вопиющей несправедливостью), рекомендации ведущих собаководов =] не изобилуют разнообразием.
Как правило, во всех подобных случаях, они являют собой предложение окунуться с головой в чрезвычайно увлекательное и полное опасностей приключение, состоящее из нескольких этапов: 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 можно, но сложнее),
так и недостатки - придется самостоятельно следить за свободным местом в локальном кэше и чистить наименее востребованные файлы.

Вот примерно так. Будет здорово, если это кому-нибудь поможет.

icq 3016467
Subject Author Posted

proxy_cache, cache miss и byte range

n0guest November 25, 2011 07:00AM

Re: proxy_cache, cache miss и byte range

Maxim Dounin November 25, 2011 07:20AM

Re: proxy_cache, cache miss и byte range

c0re November 25, 2011 07:16PM



Sorry, only registered users may post in this forum.

Click here to login

Online Users

Guests: 160
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready