1. 1
  2. 2
  3. 3
  4. 4
  5. 5

# теория · шаг 1 из 5

Приложение голышом в интернете

Коротко

nginx ставят ПЕРЕД приложением, чтобы приложение занималось только кодом.

  • Из 31 запроса за страницу код нужен ровно в одном - остальные тридцать это файлы, и отдавать их приложением значит занимать им рабочие процессы.
  • Медленный клиент занимает воркер приложения на всё время передачи. Не нагружает - именно занимает: процессор при этом простаивает.
  • TLSTLSСлой между TCP и HTTP: стороны договариваются о ключах, после чего всё содержимое запросов и ответов идёт закрытым. Стоит одного лишнего круга по сети в версии 1.3 и двух в 1.2., распределение запросов между бэкендамиБэкендПрограмма, которая на самом деле считает ответ: Django, Node, PHP, Go. nginx сам ничего не вычисляет - он отдаёт файлы и передаёт запросы бэкенду. и обновление без падения - это тоже не бизнес-логика, и держать их в коде магазина незачем.
  • Правило: всё, что не требует твоего кода, решается ДО того, как запрос дойдёт до приложения.

Если это и так знакомо - листай до «Теперь сам» в конце.

Что сломается первым

Магазин shop.local написан, запущен командой gunicorn -w 4 shop.wsgi, портПортУ одного адреса машины портов 65535; программа занимает один или несколько и слушает их. 80 - обычный HTTP, 443 - HTTPS. Именно пару «адрес и порт» занимает директива listen. проброшен наружу. Четыре рабочих процесса, четыре ядра, база не нагружена.

Приходит две сотни посетителей. Что упрётся первым: процессор, память, база или что-то ещё?

Ни то, ни другое, ни третье. Упрутся четыре рабочих процесса, и упрутся они не в вычисления, а в ожидание. Дальше разберём, почему именно так, - но сначала стоит увидеть картину целиком.

На что это похоже

Представь ресторан, где повар работает один и без официанта.

Гость зашёл - повар открывает дверь. Гость выбирает - повар стоит рядом с блокнотом. Гость мямлит и полчаса не может решить - повар всё это время стоит. Приносить тарелки, наливать воду, подавать хлеб из корзины у входа - тоже повар. Готовит он при этом отлично и быстро.

Ресторан встанет не потому, что повар плохо готовит. Он встанет потому, что готовка занимает у него десять процентов времени, а остальные девяносто он открывает двери и ждёт, пока гость определится.

nginx - это официант и швейцар. Он делает всё, что не готовка: встречает, принимает заказ целиком, приносит хлеб из корзины и уносит тарелки. Повару достаётся только то, ради чего он повар.

Механизм: где именно проходит граница

интернет nginx файлы с диска приложение :8000 TLS, медленный клиент, лимиты и буферы 30 запросов из 31 1 запрос из 31 приложение видит только быстрые локальные запросы, собранные целиком
Граница проходит там, где кончается инфраструктура и начинается бизнес-логика. Слева от приложения остаётся всё, для чего не нужен его код.

Клиент разговаривает только с nginx. Приложение живёт в тепличных условиях: к нему приходят быстрые локальные запросы, уже собранные целиком, уже расшифрованные, и только те, которые действительно требуют кода.

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

Правило

Формулировка, из которой выводится половина курса:

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

Разбор: как четыре воркера превращаются в ноль

Считаем на числах из начала урока. Четыре воркераРазбирается в разделе 1, глава «Поставить и потрогать»: Словарь: термины для тех, кто пришёл из Django или WordPress gunicorn в синхронном режиме - воркер это рабочий процесс, копия приложения, поднятая ради того, чтобы обслуживать запросы, - каждый обрабатывает ровно один запрос за раз.

Приходит клиент с телефона в подземном переходе. Он отправляет форму заказа размером 2 КБ со скоростью 200 байт в секунду:

2048 байт ÷ 200 байт/с = 10 секунд

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

Четыре таких клиента - и свободных воркеров ноль. Магазин не отвечает никому, включая тех, у кого интернет отличный. В мониторинге это выглядит издевательски: загрузка процессора пять процентов, память свободна, база скучает, сайт лежит.

Пропускная способность при четырёх воркерах и десяти секундах на запрос:

4 воркера ÷ 10 секунд = 0,4 запроса в секунду = 24 запроса в минуту

Двадцать четыре человека в минуту на четырёхъядерном сервере. Именно поэтому такой клиент называется медленным, и защищаться от него приложения обычно не умеют.

Разбор: сколько запросов вообще нужны приложению

Открой любую страницу магазина и посмотри, из чего она состоит: сам HTML, файл стилей, два шрифта, логотип, иконки, картинки товаров. Тридцать файлов - обычное дело.

Считаем, сколько из этих запросов требуют кода:

Что запрашивается Штук Нужен код магазина?
HTML страницы каталога 1 да, считает товары и цены
CSS, шрифты, иконки 6 нет, это файлы на диске
картинки товаров 24 нет, это файлы на диске

Один запрос из тридцати одного. Остальные тридцать приложение читает с диска своими руками, занимая на каждый файл тот самый воркер, который мог бы посчитать корзину.

nginx на том же файле тратит примерно ноль. Не потому, что написан на C, а потому, что не читает файл в память: он говорит ядру «возьми вот этот файл и отправь вот в этот сокетСокетТо, что операционная система заводит, когда программа начинает слушать адрес с портом или подключается куда-то. Два разных listen на один и тот же адрес с портом дают один сокет на двоих - отсюда общие для них настройки.», и данные идут из дискового кеша прямо в сеть, минуя процесс целиком. ДирективаРазбирается в разделе 1, глава «Поставить и потрогать»: Словарь: термины для тех, кто пришёл из Django или WordPress - строка настройки nginx вида «имя и значение»; эта называется sendfile on, и это не выигрыш на проценты, это другой порядок величины.

Что ломается без этого

Три симптома, за которыми стоит одна и та же причина.

«Сайт лёг, а сервер простаивает». Процессор на пяти процентах, память свободна, запросы не проходят. Воркеры заняты ожиданием, а не работой.

«После рассылки всё встало на десять минут». Тысяча человек открыли письмо одновременно, каждый потянул тридцать файлов, приложение честно встало в очередь их отдавать.

«Сайт недоступен на минуту при каждом выкате». Старый процесс убит, новый ещё не поднялся, и принять соединение в этот момент некому. Когда впереди стоит nginx, он держит соединение и ждёт, пока бэкенд вернётся.

Пока приложение одно, ничего этого не видно

Все три симптома приходят вместе с нагрузкой, то есть ровно тогда, когда сайт начал получаться. Поэтому «у меня и так работает» - это не аргумент про архитектуру, а описание текущего трафика.

Зачем это в работе

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

Второй случай - собеседование и код-ревью. Вопрос «а зачем тут nginx, если приложение и так умеет HTTP» задают часто, и правильный ответ не «так принято», а перечисление того, что снимается: файлы, медленные клиенты, TLS, выбор бэкенда, выкладка без простоя.

Чего nginx не сделает

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

Теперь сам

У приложения восемь воркеров. Средний запрос к коду обрабатывается 50 мс. Страница тянет 30 файлов, каждый отдаётся приложением за 5 мс. Сколько посетителей в секунду выдержит сервер без nginx и сколько - с ним, если считать, что файлы nginx отдаёт бесплатно?

Ответ. Без nginx одна страница стоит приложению 50 + 30 × 5 = 200 мс работы. Восемь воркеров дают 8 ÷ 0,2 = 40 страниц в секунду. С nginx приложению остаётся только сам HTML - 50 мс, то есть 8 ÷ 0,05 = 160 страниц в секунду. Вчетверо больше на том же железе, и это ещё без учёта медленных клиентов, из-за которых цифра «без nginx» на практике оказывается гораздо хуже расчётной.

Главное

nginx стоит перед приложением и забирает всё, что не требует кода: файлы, ожидание медленных клиентов, TLS, выбор бэкенда, выкладку без простоя. Выигрыш даёт не язык, на котором он написан, а то, что работа перекладывается на ядро и не доходит до твоих воркеров.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.