# теория · шаг 1 из 4
Как файл уезжает клиенту
Коротко
Для больших файлов настройка передачи зависит от диска, памяти и скорости клиента.
- sendfile позволяет передавать файл без копирования через буфер пользовательского процесса; проверь, включён ли он в твоей сборке и конфиге.
- aio threads помогает вынести блокирующее чтение, а directio меняет работу с файловым кешем. Их эффект нужно измерять.
- limit_rate_after позволяет начать ограничение скорости после заданного объёма.
- 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_rate ограничивает один запрос, а не соединение и не клиента: по
одному соединению HTTP/2 идут параллельные запросы, и полоса у каждого своя.
Суммарную скорость клиента держит связка с limit_conn из двенадцатого раздела:
в HTTP/2 она считает соединением каждый одновременный запрос, и perip 2 даёт
не больше двух полос по 2m. Это честная защита канала: скачивание идёт, но
один клиент не съедает всю полосу. Значение можно взять из
переменной - например, отдавать быстрее авторизованным:
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, временные файлы, предел скорости и кеширование частей решают разные задачи. Сначала проверь действующий конфиг и путь данных, затем измерь эффект одной настройки на характерном размере файла и скорости клиента.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий