# теория · шаг 1 из 4
Как файл уезжает клиенту
Коротко
Отдать файл кажется тривиальным. На гигабайтном образе разница между «тривиально» и «правильно» - это разы по процессору и памяти.
sendfile onубирает копирование через процесс. В официальном образе он уже включён - проверьnginx -T | grep sendfile, прежде чем добавлять.aio threadsиdirectioнужны там, где файлы не помещаются в память. На обычном сайте они бесполезны, аdirectioвредит.limit_rateсlimit_rate_afterрежет только длинные закачки: замер даёт 3,0 секунды против 1,0 на том же файле.sliceрежет большой файл на куски, которые кешируются по отдельности.
Если знаешь, что делает sendfile, и не путаешь limit_rate с limit_req - листай до «Теперь сам».
Сначала ответь сам
В чужом конфиге первой строкой стоит sendfile on;, и автор гордо называет это
оптимизацией. Что она даёт на самом деле?
Ничего нового - она уже стоит. Официальный образ nginx:
sendfile on;
Строка живёт в nginx.conf из коробки много лет. Полезной она становится
только там, где кто-то её выключил, - а такое бывает: sendfile off иногда
ставят в контейнерах поверх сетевых файловых систем, где механизм работает
неверно.
На что это похоже
Курьер, который забирает коробку со склада, привозит её в офис, ставит на стол, берёт со стола и несёт в машину. Коробку никто не открывал и ничего в ней не менял - но она дважды прошла через руки.
sendfile - это просьба к складу отгрузить прямо в машину. Данные из файла
уходят в сокет внутри ядра, не проходя через память процесса.
Механизм: где живут данные при отдаче
Правило
sendfile on;
tcp_nopush on;
tcp_nopush работает только вместе с sendfile и просит систему подождать,
пока наберётся полный пакет: заголовки ответа и начало файла уезжают одним
пакетом вместо двух. Мелочь - но на раздаче тысяч файлов это тысячи лишних
пакетов.
Рядом стоит tcp_nodelay on (включён по умолчанию), и они не конфликтуют:
nopush действует, пока идёт тело через sendfile, nodelay - на остальном.
Разбор: когда sendfile мешает
У sendfile есть слабое место - он блокирующий. Пока ядро тянет с диска
холодный блок, рабочий процесс стоит. На SSD и горячем кеше это незаметно, на
раздаче медиатеки с обычных дисков - очень даже.
aio threads;
directio 16m;
output_buffers 2 1m;
aio threads уводит чтение в пул потоков: воркер отдаёт задачу и продолжает
обслуживать других клиентов. directio 16m велит читать файлы крупнее
16 мегабайт мимо кеша страниц - большие файлы всё равно вытеснят из кеша всё
полезное, а читаются один раз.
Обе возможности требуют флагов сборки, и в официальном образе они есть:
nginx -V 2>&1 | tr ' ' '\n' | grep -E 'file-aio|threads'
--with-file-aio
--with-threads
Это настройки под конкретную нагрузку, а не «сделать быстрее»
aio и directio осмысленны там, где раздаются файлы, не помещающиеся в память
целиком: видео, образы, архивы. На сайте с картинками и css они не дадут ничего,
а directio даже навредит - отключит кеш там, где он работал.
Разбор: сколько именно режет limit_rate
Замер на файле в 2 МБ, полное время скачивания:
| настройка | время |
|---|---|
| без ограничения | 0,003 с |
limit_rate 512k |
3,01 с |
limit_rate 512k плюс limit_rate_after 1m |
1,00 с |
Третья строка и есть рабочий рецепт: первый мегабайт уезжает на полной скорости - страница и превью не пострадают, - а дальше скорость режется.
limit_rate_after 10m;
limit_rate 2m;
limit_conn perip 2;
В паре с limit_conn из двенадцатого раздела это честная защита канала:
скачивание идёт, но один клиент не съедает всю полосу. Значение можно взять из
переменной - например, отдавать быстрее авторизованным:
map $http_authorization $rate {
default 1m;
~. 5m;
}
Что ломается без этого
Отдельная история - когда большой файл ещё и кешируется с бэкенда. Целиком он занимает зону и тянется одним куском; если клиент попросил середину файла, кеш от этого не помогает вовсе.
slice 1m;
proxy_cache media;
proxy_cache_key $uri$slice_range;
proxy_set_header Range $slice_range;
proxy_cache_valid 200 206 1h;
Модуль slice режет запрос на куски по мегабайту, и каждый кусок кешируется
отдельной записью. Перемотка видео на середину тянет два-три куска вместо
гигабайта, а параллельные зрители одного фильма греют один и тот же кеш.
Обрати внимание на 206 в proxy_cache_valid: куски приезжают именно
частичными ответами.
Зачем это в работе
Практическое правило простое: из всего перечисленного по умолчанию нужно ровно
ничего. sendfile уже включён, tcp_nopush даёт мелочь, а aio, directio,
limit_rate и slice включают под конкретную задачу, которую сначала видно в
метриках.
Признак, что задача есть: $request_time большой при маленьком
$upstream_response_time - то есть время уходит на отдачу, а не на приложение.
Как это померить, разбирает следующий урок.
Теперь сам
На сервере раздачи дистрибутивов включили limit_rate 1m на весь server.
Через день жалуются, что сайт стал медленным - не скачивание, а обычные
страницы. Что произошло?
limit_rate без limit_rate_after режет ответ с первого байта, включая HTML,
css и картинки: страница на 300 КБ теперь едет треть секунды вместо мгновения.
Правильно - вешать ограничение на location с файлами и добавлять
limit_rate_after, чтобы мелочь проскакивала на полной скорости.
Главное
sendfile on убирает копирование файла через процесс и в официальном образе уже
включён - проверяй nginx -T перед тем, как «оптимизировать». tcp_nopush
работает только вместе с ним и склеивает заголовки с началом файла. aio
threads и directio нужны только там, где файлы не помещаются в память; на
обычном сайте они бесполезны, а directio вредит. limit_rate без
limit_rate_after режет и обычные страницы: замер даёт 3,0 секунды на файл в
2 МБ против 1,0 с порогом в мегабайт. slice режет большой файл на куски,
которые кешируются по отдельности и делают перемотку дешёвой.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий