MyTetra Share
Делитесь знаниями!
Кешируем статику
Время создания: 13.07.2018 15:31
Текстовые метки: nginx cache
Раздел: Nginx

Существует мнение, что nginx — отличный инструмент для отдачи статики.
Есть статьи, где описываются настройки sendfile или
aio для «улучшения» отдачи.
На Хабре есть чего почитать о настройке proxy_store с proxy_cache для минимизации проблем со стороны мозгов сайта.
Еще в QA иногда возникают вопросы про
кеширование картинок, например.

Зачем заниматься этой ерундой! — говорят опытные пользователи — OS лучше вас знает как кешировать файлы! С кешем и префетчем в современных OS, точнее FS, проблем нет! Зачем плодить свои кеши и списки популярных материалов и все такое?...


Есть только одно вредное
«но» — в среде исполнения nginx (в общем случае Linux) понятие "файл" и вообще «файловая система» — просто понятие.
И однажды, когда я, подмонтировав сервер по sshfs, обновил один скриптик, случилось волшебное:
1. На каждой страничке стало на 4 картинки больше.
2. Сервера сдохли.

Что поделать — картинки хранились на glusterFS. Наступил полный FUSE.

GlusterFS, как и интерфейс FUSE, сильно сжирает производительность, но он того стоит.
Изначально у нас был один сервер. Потом два, второй из который монтировал www с первого по nfs. Потом три, четыре.
Не очень надежно, но так уж исторически сложилось. Для спасения ситуации нужна была некая совершенно прозрачная система, in-place замена nfs. Кластерная ФС.
Сейчас на всех серверах стоит raid 10, и за полтора года эксплуатации он пару раз падал. В том числе совсем. Gluster молча поднимал копию ФС на новой железяке и все продолжало работать. Гластер свою работу знает.

Но на самом деле история начиналось немного не так.


Причина была другая, но решения проблем будет совпадать.
Нет ничего страшнее чем то что "
исторически сложилось".
И в одном моем проекте «исторически сложилась» ситуация, когда картинки отдает php.
В свое время нужно было сделать файловый загрузчик с различными ACL, ресайзами и вотермарками. Это было почти 10 лет назад. Механизм прижился и с тех пор не менялся.
Сейчас для решения таких проблем есть много специализированных решений(
elliptics, backpack и другие; а еще лучше CDN). Но тратить человекомесяцы на переход не хочется. Спать хочется, а не работать.
Да и php чтука, в принципе, неплохая. Но имеет свои пределы.

Но проблема тормозных бэкендов решена


Хабр, в очередной раз подсказал вариант решения —
proxy_store и proxy_cache.
Но proxy_store использовать мне нельзя — сохранить негде, и с proxy_cache тоже проблемы.
Есть одно, большое такое, «но» — много лет назад я уже столкнулся с производительностью отдачи файлов и решил «пускай лучше nginx отдает конечные файлы за меня».
X-Accel-Redirect.
Проблема — nginx не может закешировать ответ из прокси если это не ответ, а команда.
X-Accel-Redirect — не кешируется.
Выхода нет?

Исходную проблему зафиксировали


Теперь вернемся к началу топика.
Нам нужно отдать статику, на этот раз без вмешательства «умных» бэкэндов. Просто статику, но с glusterfs.
Директив для кеширования статики просто нет — не предусмотрены. Считается что OS сама не дура. Для статики есть только open_file_cache и настройка самой передачи файлов(sendfile/aio и компания).

Что делать?
Усложним задачу, и попробуем решить проблему исключительно средствами nginx, не внося никаких изменений в другие коды.

обычный конфиг


# Static files location

location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|js|swf|txt|ico)$ {

root /var/www/kashey;

}




Пропатченый конфиг


Nginx делает proxy_pass сам на себя, кешируя результат. Возможно тут можно использовать fcgi, но так — нагляднее.

# Static files location

location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|js|swf|txt|ico)$ {

set $dopass 1;

proxy_set_header Host $host;

#если у нас есть "кука" - отдаем файл с реальной ФС

if ($http_fiberpass) {

root /var/www/kashey;

#или proxy_pass на бэкэнд

set $dopass 0;

break; #но этот break не работает

}

#настройки кеширования, не забудьте описать proxy_cache_valid

proxy_cache static_cache;

#добавляем прокси "куку"

proxy_set_header fiberpass "nginx";

if ($dopass) {

#отправляем запрос к самому себе.

proxy_pass http://127.0.0.1:80;

}

}


И никакой магии.
В случае с X-Accel-Redirect схема такая:

1. nginx получается запрос
2. пересылает себе, к сожалению запрос полный и реальный.
3. пересылает на прокси
4. получает ответ и отдает файл
5. ответ кешируется
6. ???
7. PROFIT


В случае с обычной статикой сложнее — root не заканчивает выполнение, break и return также не работают. Настройка прокси не может находиться внутри условного блока.

Что это дает?


В смысле с X-Accel-Redirect с apache снимается вал запросов. На загруженной системе разница может составить 50 раз.
Но это понятно — php сам по себе, а через апач тем более, не самое реактивное решение.
Latenсy запроса меньше 40мс, лично у меня, не получался (общее время php+gluster).
В случае использования варианта с proxy_cache скорость отдачи приравнивается к скорости отдачи статики.

А что со статикой?


Отдача с gluster — 13мс — это нормальное время для системы без кеша.


Отдача из кеша — 1мс — это замечательное.


Важно учесть — сервера находятся в Германии, а клиенты в России.
Нормальные пинги до сайта 60-100мс.
Так что для клиента ускорение практически не заметно.

Но оно заметно для сервера.
1. Снизилась нагрузка на сервера, в том числе в попугаях Load average и soft-irq.
2. Снизился трафик между серверами (можно решить включив WORM(Write Once Read Many volume), но это уже не «прозрачно»).

3. Все стало работать чуть быстрее.

В итоге работы которые на 10% снизили скорость реакции на клиенте, получились в результате 10-ти кратного ускорения отдачи статики. Что снизило общую нагрузку на сервера примерно раз в 20.
Обратное утверждение, к сожалению, тоже будет верно — сколько не старайся, клиент может и не заметить.

PS: Многие знакомые сисадмины даже не допускали мысли о возможности работы такой схемы. Не позволяет кешировать/проксировать — значит нельзя.

Хорошие программисты не всегда хорошие сисадмины, а сисадмины, обычно, вообще не программисты.
Настоящие герои всегда идут в обход, в брод, и на велосипедах.

PPS: я, кстати, не сисадмин. И, быть может, все сделал не правильно.

Метки:

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Реклама

Читают сейчас

4,1k 4

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Реклама


+26

35,0

Карма

6,5

Рейтинг

34

Подписчики

Корзунов Антон kashey

javascript, webgl, maps, databases

Поделиться публикацией

Похожие публикации

+47 1,5k 31 47

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Реклама

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


+11

if'ы зло.
Рекомендуется использовать if только в случае если вы из него уходите (rewrite last, 30x redirect и т.п.). Это логично и это почти никогда не сломается в случае изменения логики в работе сервера.

Use try_files if it suits your needs. Use the «return ...» or «rewrite… last» in other cases. In some cases it's also possible to move ifs to server level (where it's safe as only other rewrite module directives are allowed within it).



Альтернативный вариант (на мой взгляд лучше читается):

server {

listen 127.0.0.1:80;

server_name nocache.domain.tld;

location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|js|swf|txt|ico)$ {

allow 127.0.0.1;

deny all;

root /path/to/fuse/filesystem;

}

}


server {

listen 80;

server_name domain.tld

location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|js|swf|txt|ico)$ {

proxy_cache static_cache;

proxy_cache_valid 200 1y;

proxy_set_header Host nocache.$host;

proxy_pass http://127.0.0.1;

}

}


+2

Стоит признать — элегантнее.


+2

  • Да тут даже дело не в элегантности. Практика распределения через location-ы, а не регулярки сложилась не просто так. Так быстрее. Ну и смотрится логичнее.

ToSHiC 15.11.13 в 17:56


+6

Вместо

allow 127.0.0.1;

deny all;



лучше писать

internal;




Ну а вообще, конечно, отдавать картинки через X-Accel-Redirect, а потом наворачивать поверх конфиги для нгинкса… Мсье (автор, не вы) знает толк в извращениях.


0

Так исторически сложилось — мне на вход приходит номер, я его преобразую в имя файла и выплевываю.
Когда я делал похожую систему для gdetotdom.ru — админ меня сразу завернул(
«нет, давай лучше так делать не будем»).
И там nginx «канонически» работает через try_files.
Если бы я научился считать md5 в nginxе — смог бы делал тоже самое. Но не научился.

  • ToSHiC 15.11.13 в 20:12


+1

Вы про штуки типа github.com/simpl/ngx_http_set_hash?

Можно даже сделать средствами перла, ничего не добавляя в стандартную сборку:

perl_set $md5 '

use Digest::MD5 qw(md5_hex);

sub {

my $r = shift;

my $some_var = $r->variable("some_var");

return md5_hex($some_var);

}

';


server {

listen 8081;

location / {

set $some_var $arg_text;

return 200 $md5;

}

}




Но всё это не отменяет того, что стоит переписать код, который формирует ссылки на картинки.


0

  • Я вот все перлом хочу, и все никак не сделаю.
    В принципе осталось узнать как substr сделать и можно будет переходить на try_files.
    Хотя у wikimapia это, в итоге, не получилось — на их объемах try_files тупит.
    Выкрутились хитро, быть может расскажут когда-нибудь.

Aecktann 15.11.13 в 21:27


+1

Здесь нельзя написать internal, потому что это не внутреннее перенаправление и не реврайт. Это прокси само на себя, с точки зрения nginx запрос абсолютно стандартный.


+1

Я стесняюсь спросить, но что побудило вас написать это правило внутри блока server c listen 127.0.0.1:80;? =)


+4

Был напуган :)


+3

  • Nginx может слушать и проксировать на unix-сокете.

listen unix:/var/run/nginx.sock;



proxy_pass http://unix:/var/run/nginx.sock;

VBart 15.11.13 в 17:56


+2

Ужасная же регулярка: ^.+\.(jpg|jpeg|gif|png|ico|css|js|swf|txt|ico)$, почему бы хотя бы для статьи не поправить очевидные косяки? И так много у вас ico на сервере (аж дважды в регулярке встречается)?

location ~* \.(?:jpe?g|gif|png|ico|css|js|swf|txt)$ { .. }



Но адекватные люди хранят статику отдельно.


0

Что сказать? — Админа на мыло — в оригинале(я немного укоротил правило для статьи) еще и два png,exe и wav с bmp
(Как хорошо, что этот админ не я)

IRainman 18.11.13 в 21:43


0

Вот кстати о ужасах данной регулярки, а почему, к примеру просто не добавить в исключения нужное, к примеру так:

location ~* \.(?!php)



Или даже листом из необходимого: php, html, shtml (для SSI в апаче), и что там ещё м.б.


+1

  • Наверное потому, что negative look-ahead не самая тривиальная конструкция в PCRE. Вот, в частности, и вы ошиблись, так работать не будет. =)

    Правильно:

location ~* ^(?!.*\.php)

IRainman 18.11.13 в 22:07


0

Ну т.е. как то совсем сурово — это вот так:

location ~* \.(?!php|html|shtml) {

root xxx;

}


+1

Опять же, правильный вариант:

location ~* ^(?!.*\.(?:php|s?html)) {

root xxx;

}

  • IRainman 18.11.13 в 22:39


0

Упс, благодарю, агу, косячу.

IRainman 24.11.13 в 08:23


0

  • Забавно, попробовал сейчас у себя на сервере так, вроде всё хорошо, но перестали работать индексы (index.php и index.shtml), ну точнее именно если так написать, то всё хорошо, а вот если просто косая черта в конце, то не работает, а nginx выдаёт 403 ошибку :) Вот уж точно не тривиальные исключения, и дело наверное даже не в регулярках.

конфиг nginx



До этого просто из головы написал… то ли лыжи не едут, то ли у меня мозгов не хватает, хотя какие то ли, конечно же второе )))

Точно, руки у меня точно по этому вопросу растут не оттуда откуда следует, ибо я так ни разу и не настроил nginx как кеширующий прокси у себя ^_^'

zuborg 15.11.13 в 18:53


0

Эта экономия на редизайне архитектуры может Вам потом вылезти боком. Это уже проявляется, на самом деле, например, в необходимости такого рода хитрозакрученных костылей, что описаны в статье. Дальше поддерживать приемлемый уровень производительности будет ещё сложнее, увы. Вместо «хочется спать» будет противоположное.

Все ИМХО, разумеется ;)

Borro 16.11.13 в 01:13


+1

А почему нельзя поставить на сервера, с которых вы монтируете файловые системы, nginx и уже их nginx'ы проксировать?


+1

Именно так и стоит.
На каждом сервере стоит и гластер-мастер и гластер-клиент. Один большой сетевой raid-1.
Но читать из «реальной» папки размещения файлов гластера нельзя — он разваливается. Читать можно только из папки клиента, а это уже FUSE раздел.


0 И у меня не так много серверов чтобы выстраивать два эшелона фронтов. Сейчас все нгинксы читают данные с себя.
А до конфига с locations я, в свое время, не додумался.

Так же в этом разделе:
 
MyTetra Share v.0.53
Яндекс индекс цитирования