Как стать автором
Обновить

Комментарии 30

"«специально обученный» Nginx"

Здесь имеются ввиду нестандартные патчи или конфигурирование?

PS. Понял. Это — конфигурирование.
Да, Nginx должен быть собран с модулями ngx_http_ssi_module, ngx_http_memcached_module и соответствующий конфиг конечно, его пример есть в демо приложении.
Но самое главное — это стандартные модули. Т.е. решение относительно универсальное.
Да эти модули есть во всех популярных дистрибутивах Linux.
В дополнение, небольшая инструкция как запустить демо.
Возможно кто-то захочет посмотреть, что из себя представляет Flask
приложение. Пример для Linux, работу в Windows не проверял. По идее
должно и там заработать.
mkdir demo
cd demo
virtualenv env
source env/bin/activate
git clone https://github.com/AleshGood/Flask-Fragment.git
cd Flask-Fragment
python setup.py develop
pip install git+https://github.com/jaysonsantos/python-binary-memcached.git
cd demo
pip install -r requirements.txt
python ssiblog.py create_db

Теперь если в коде ssiblog.py найти и закомментировать строку
FRAGMENT_CACHING = True или выставить значение False, приложение можно
запустить командой:
python ssiblog.py debug

В этом случае кэширование будет отключено, но приложение будет
нормально работать по урлу 127.0.0.1:5000/

Что бы посмотреть кэширование, надо в Nginx добавить конфиг nginx.conf,
возможно поменять в нем имя хоста и порт. По умалчиванию приложение
«нагло садится» на localhost:80. В системе так же должен быть доступен
memcached на порту 11211 без авторизации (либо править конфиг приложения).
FRAGMENT_CACHING нужно выставить в True.
=== В системе так же должен быть доступен memcached на порту 11211

не на порту 11211, а на 127.0.0.1:11211, потому, что как совсем не надо что б мемкеш наружу торчал. равно как и другие сервисы.
Нет большого смысла в memcached на локальной машине. Это скорее решение когда к одному общему кеширующему серверу обращается множество фронтендов.
Вообще да, наиболее хорошо это будет смотреться в системе с несколькими фротендами разделяющими нагрузку. Но из-за того что кэш получился управляемый — это хорошо «развеселит» приложение работающее и на одной машине.

Nginx конечно же крут в кэшировании, но насколько я знаю сбросить кэш программно для определенного урла нельзя. Можно конечно удалять файлы из кэшсторе, но как определить, какой файл надо удалить, что бы обновился кэш для определенного урла? Хотя могу заблуждаться, и вы что-то уже сделали в этом направлении. Но инфы такой не встречал.
Замените в вашем коде операции с memcached на операции с файлами и получите более эффективное и надежное решение. В конфигурации nginx при этом нужно просто будет заменить mecached_pass на try_files (или даже без него, зависит от конкретных настроек).
Есть что почитать по этой теме? Для конфигурации одно приложение, один хост — это было бы наверно оптимальным вариантом.
Про что именно? С точки зрения nginx будет просто набор статических файлов, которые он будет раздавать. Как этими файлами управлять — полностью на совести приложения (т.е. на вашей). Разве что вы можете поручить nginx самим сохранять файл, а удалением (т.е. инвалидацией вашего кэша) будет заниматься приложение, это просто настраивается с помощью proxy_store (или fastcgi_store).

Location, которые принимает запросы может выглядить как-то так:

location /cache/ {
    internal;
    root /path/to/cache/;
    error_page 404 =200 @backend;
}


На Python вы можете не изобретать велосипед, а использовать готовые решения, только адаптировав их под раздачу самих файлов уже nginx-ом:

Я не совсем понимаю следующие моменты, как их реализовать только с помощью Nginx:

Например есть SSI фрагмент с урлом /_inc/posts_list/4 содержащий четвертую страницу списка постов. У меня пользователь добавил пост, как мне изменить кэш конкретно для этого URL в кэшсторе Nginx?

Удалить мне его нельзя, содержимое может генерироваться пару секунд. В это время соответственно Nginx пока должен отдавать старый кэш.

И еще вопрос, можно ли организовать защиту от raсe condition на време генерации обновленного контента? В моем примере для этих целей используется тот же memcached.

Flask-Cache не поможет реализовать фрагментарное кэширование, я его серьезно поковырял на этот предмет, более того я его пользовал для обычного кэширования и знаком с его функциональностью.

Beaker Cache, не знаю, но его упоминание мне не попадалось к контексте SSI/ESI.
кэш конкретно для этого URL в кэшсторе Nginx
В данной схеме nginx ничего не знает про кэш, он просто отдает файл /path/to/cache/posts_list/4. Соответственно вы в своем приложении генерирует страницу, записываете её во временный файл, а затем моментально, атомарной операцией заменяете этим временный файлом /path/to/cache/posts_list/4.
f = NamedTemporaryFile(delete=False)
f.write('Hello\n')
f.close()
os.rename(f.name, "/path/to/cache/posts_list/4")

Никакого race condition тут нет, а nginx будет отдавать старый элемент кэша до тех пор, пока вы готовите новый. Но вообще вы можете атомарно дописывать в файл.
В принципе получиться должно, но 15 строчками кода вряд ли обойдется. :)

Надо будет как-то решать проблему права доступа к файлу, сделать свой велосипед для установки блокировки, пока генерируется контент и т.п. В случае с мемкашэм, это всё можно «переложить на его плечи».
установки блокировки, пока генерируется контент и т.п.

Что под этим подразумевается?
Когда один процесс/поток обнаружил, что кэш устарел и начал формировать контент, чтобы обновить его. Другие процессы/потоки обнаружившее этот факт не должны начать делать тоже самое, а просто отдавать пока старое содержимое.

В случае с использованием только Nginx, это придется наверно делать как-то с использованием файловой системы, не будем же мы для этого задействовать тот же мемкэш от которого хотим отказаться или подобные сервисы?
Так возможности файловой системы в *nix побогаче будут, чем возможности memcached, порядком.

Вместо того, чтобы создавать какие-то отдельные ключи, в случае с файлами вы можете навешивать лок прямо на элемент кэша (т.е. на сам файл).

Читать man fcntl про Advisory locking или более простой man 2 flock. А Python есть соответсвующие функции.
В общем понятно в общих чертах, если «хватит здоровья» попробую и такой режим прикрутить.
Есть ещё другой способ реализовать лок. Использовать open() с флагами O_CREAT|O_EXCL в этом случае, если файла не существует, то он будет создан, а если уже существует, то вызов open() вернет ошибку EEXIST.

В итоге получается так, создаем временный файл (допустим он будет лежать в том же каталоге и иметь то же имя, но только с тильдой на конце):
item = "posts_list/4"
cache = "/path/to/cache"

item_file = os.path.join(cache, item);
tmp_file = item_file + "~"

try:
    fd = os.open(tmp_file, os.O_CREAT | os.O_EXCL)
    try:
        os.write(fd, 'Hello\n')
        os.rename(tmp_file, item_file)

    except:
        os.remove(tmp_file)
        raise

    finally:
        os.close(fd)

except OSError as e:
    if e.errno != errno.EEXIST:
        raise
 
    print "cache is locked"
Таки можно сбросить кэш конкретного URL
В конфиге пишем примерно это:
proxy_cache_bypass $http_x_update;

сбрасываем программно так: curl -s -o /dev/null -H «X-Update: 1» www.example.com

естесственно X-Update лучше заменить на что-то свое
То да curl рулит, но у меня сброс кэша делается из кода приложения самим расширением путем вызова метода. Причем не надо знать URL, достаточно указать имя вьюшки, которая генерировала этот контент
Впрочем конечно это можно сделать и из кода, спасибо за наводку. Поэксперементирую с этим. Тут на мой взгляд остается только вопрос как сделать блокировку для thread safe.
Есть параметр, которые отвечает за количество потоков, которые одновременно могут обновлять кэш.
Если вы конечно об этом
Не, в том что мы с VBart обсуждали.
ну, я до этого догадался года два или три назад habrahabr.ru/post/109050/
странно, что ты не нашел это на Хабре
Во первых твое решение имеет тот же «фатальный недостаток», что и те которые я привел ссылками в начале статьи:)
Во вторых решений для PHP по теме много, тот же симфони практически поддерживает ESI+Varnish «из коробки».

Ну а потом самое главное, мое решение реализует динамический кэш, тоеть приложение имеет возможность сбросить/поменять содержимое кэша в зависимости от логики своей работы. Если этого нет, то memcashed лишнее звено в этой цепочки, Nginx и так прекрастно может кэшировать SSI фрагменты.
жаль, что ты невнимательно прочитал мою статью
Подсчет количества сообщений пользователя достаточно затратная операция, а если мы там выводим еще и граф друзей, то только один этот фрагмент существенно просадит БД, а следовательно и общую скорость загрузки страницы… Можно закэшировать контент этого блока...

Очень неудачный пример. У каждого пользователя свое количество сообщений, поэтому этот фрагмент для каждого пользователя индивидуален. Т.е. получается, что кешировать данный фрагмент придется для каждого юзера отдельно, что технически, в общем-то, осуществить не трудно, но на высоконагруженных проектах вряд ли годится.
Очень неудачный пример. У каждого пользователя свое количество сообщений, поэтому этот фрагмент для каждого пользователя индивидуален.


Да индивидуален, но например в демо к моему решению он занимает ~400байт. Хорошо, пусть в навороченных соцсетях он 40Кбайта. При количестве онлайн пользователей 10000, для первого случая объем требуемого ОЗУ под это дело в первом случае ~4Мбайта, во втором ~400Мбайт. Это же легко потянет кэш на одной машине, даже на порядок больше, не?
А если у нас десяток таких машин с расшарденым мемкэшем?
Как раз только несколько дней назад примерно такое делал.
Идея была в том, чтобы код страницы, сгенерированной фреймворком MODX полностью сохранять в memcached. А а дальнейшем при заходе по этому же адресу (когда код страницы уже есть в мемкеше), чтобы nginx получал напрямую из memcached и сразу отдавал клиенту без обращения к php.
Результат можно покликать здесь. (при кликах стоит учитывать, что не все страницы в кеше есть, так что для надежности кликайте главное меню).
Так же следует учитывать, что сервер в Амстердаме. У меня из Москвы туда пинг 40 мсек. Ответ от сервера файрбаг фиксирует за 60 мсек. То есть ответ примерно за 20 мсек.
Самое сложное здесь было — конфиг для nginx-а. И еще пара подводных камней было. Все подробно описал в статье.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории