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

# теория · шаг 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:

↳  выводnginx -T | grep sendfile
    sendfile        on;

Строка живёт в nginx.conf из коробки много лет. Полезной она становится только там, где кто-то её выключил, - а такое бывает: sendfile off иногда ставят в контейнерах поверх сетевых файловых систем, где механизм работает неверно.

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

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

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

Механизм: где живут данные при отдаче

без sendfile файл буфер процесса сокет две копии с sendfile файл сокет ядро перекладывает само ни одной
На раздаче статики это самая дешёвая оптимизация - и потому она включена по умолчанию.

Правило

/etc/nginx/nginx.confhttp
sendfile   on;
tcp_nopush on;

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

Рядом стоит tcp_nodelay on (включён по умолчанию), и они не конфликтуют: nopush действует, пока идёт тело через sendfile, nodelay - на остальном.

Разбор: когда sendfile мешает

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

/etc/nginx/conf.d/media.confhttp server location /video/
aio       threads;
directio  16m;
output_buffers 2 1m;

aio threads уводит чтение в пул потоков: воркер отдаёт задачу и продолжает обслуживать других клиентов. directio 16m велит читать файлы крупнее 16 мегабайт мимо кеша страниц - большие файлы всё равно вытеснят из кеша всё полезное, а читаются один раз.

Обе возможности требуют флагов сборки, и в официальном образе они есть:

$  команда
nginx -V 2>&1 | tr ' ' '\n' | grep -E 'file-aio|threads'
↳  вывод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 с

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

/etc/nginx/conf.d/media.confhttp server location /downloads/
limit_rate_after 10m;
limit_rate       2m;
limit_conn       perip 2;

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

/etc/nginx/nginx.confhttp
map $http_authorization $rate {
    default  1m;
    ~.       5m;
}

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

Отдельная история - когда большой файл ещё и кешируется с бэкенда. Целиком он занимает зону и тянется одним куском; если клиент попросил середину файла, кеш от этого не помогает вовсе.

/etc/nginx/conf.d/media.confhttp server location /video/
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 режет большой файл на куски, которые кешируются по отдельности и делают перемотку дешёвой.

Комментарии

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

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

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