iperf3 показывает гигабит, а файл качается медленно: чем эти два теста отличаются
Вы гоняете iperf3 между своим сервером и клиентом, видите ровный гигабит (или сколько там заявлено в тарифе), радуетесь — а потом скачиваете реальный файл по HTTP или HTTPS и получаете в два-три раза меньше. Первая реакция — «сеть врёт» или «провайдер режет канал». На практике почти всегда всё прозаичнее: iperf3 и одиночная загрузка файла измеряют принципиально разные вещи, и расхождение между ними в несколько раз — это норма, а не повод писать в поддержку хостера. Разберём, что именно тестирует каждый инструмент, откуда берётся разница и как быстро найти реальное узкое место.
Содержание
Что на самом деле измеряет iperf3
iperf3 — это синтетический тест пропускной способности канала между двумя точками, специально настроенными под этот тест: демон iperf3 -s на одной стороне и клиент iperf3 -c на другой. Никакого диска, никакого веб-сервера, никакого TLS — данные генерируются в памяти на одном конце и выбрасываются в памяти на другом. Это тест «трубы» в чистом виде, и именно поэтому он показывает цифру, близкую к теоретическому пределу канала.
Ключевой момент — количество параллельных TCP-потоков. По умолчанию iperf3 использует один поток (-P 1), но почти любая инструкция «как правильно померить канал» рекомендует поднять -P до 4, 8 или больше. Причина проста: на линках с высокой задержкой или большой номинальной скоростью один TCP-поток часто физически не может выбрать всю полосу — упирается в размер окна (подробнее — в статье про TCP-окно и длинный толстый канал). Несколько параллельных потоков обходят это ограничение: каждый получает свою долю полосы, а хостер или инженер складывает их и видит цифру, близкую к номиналу канала.
# один поток — честный "потолок одного TCP-соединения"
iperf3 -c server.example.com -P 1 -t 20
# восемь параллельных потоков — суммарная ёмкость канала
iperf3 -c server.example.com -P 8 -t 20
Если вы (или админ, который присылал вам скриншот) гоняли тест с -P больше единицы — вы видели сумму нескольких соединений, а не то, что способна выдать одна HTTP-загрузка. Ровно по этой же причине классические спидтесты (Ookla и аналоги) намеренно открывают несколько параллельных соединений: они и заявлены как измерение ёмкости канала, а не скорости одного файла.
Отдельно стоит держать в голове UDP-режим iperf3 (-u) — он вообще не подчиняется логике TCP-окна и показывает совсем другие цифры, но это уже вопрос потерь пакетов и pps-лимитов, а не сравнения с HTTP-загрузкой.
Один TCP-поток живёт по правилам окна и BDP
Загрузка файла по HTTP или HTTPS — это, за редкими исключениями, одно TCP-соединение. Даже в HTTP/2 с его мультиплексированием логических потоков внутри одного TCP-соединения физический канал остаётся один. А у одного TCP-соединения есть жёсткий потолок пропускной способности: он определяется размером окна (window size) и произведением полосы на задержку (bandwidth-delay product, BDP).
Грубо говоря, максимум, который может протолкнуть одно соединение — это размер_окна / RTT. На коротком локальном линке с RTT в единицы миллисекунд это почти не заметно: окно успевает "провернуться" много раз в секунду. А вот на канале до удалённого региона — трансатлантика, кросс-регион между дата-центрами — та же математика начинает резать скорость одного потока в разы, даже если сам канал физически способен на гигабит и больше. Подробный разбор формулы, дефолтных размеров окна в Linux и того, как их тюнить — в статье про TCP-окно и длинный толстый канал.
Практический вывод: если iperf3 -P 1 и однопоточная HTTP-загрузка показывают близкие цифры, а iperf3 -P 8 кратно выше — вы не столкнулись с "испорченным каналом", вы упёрлись в потолок одного TCP-потока. Лечится либо тюнингом окна (net.ipv4.tcp_rmem, net.ipv4.tcp_wmem, net.core.rmem_max на обеих сторонах), либо параллельной загрузкой в несколько соединений (менеджеры закачек, aria2c -x N, диапазонные HTTP-запросы Range).
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверДиск на сервере как скрытое узкое место
iperf3 вообще не трогает диск: и клиент, и сервер работают целиком в оперативной памяти. Реальная загрузка файла — это чтение файла с диска (или из page cache, если файл туда уже попал) и отдача его в сокет. Если файл большой и не помещается в кеш, или диск на сервере и так нагружен другими процессами, скорость чтения с диска вполне может оказаться ниже, чем то, что способен пропустить сетевой канал — и тогда именно диск, а не сеть, определяет скорость скачивания.
Проверяется это отдельно от сети — просто замером чтения на самом сервере:
# грубая прикидка — но first run часто читает из кеша, а не с диска
dd if=/path/to/big-file.bin of=/dev/null bs=1M status=progress
# честнее — обход page cache там, где файловая система это позволяет
dd if=/path/to/big-file.bin of=/dev/null bs=1M iflag=direct status=progress
dd даёт быструю прикидку, но для честного замера дисковой подсистемы (особенно если нужно повторяемо гонять последовательное и случайное чтение) удобнее fio — он умеет явно задавать количество потоков, глубину очереди и работать в обход кеша. Как собрать такой замер и на что смотреть в выводе — в статье про fio для честного замера диска. Если цифры из fio для одного читающего потока близки к тому, что вы видите при скачивании файла по HTTP — узкое место найдено, и это не сеть.
Отдельно стоит проверить, не первый ли это запрос к файлу вообще: если файл только что записан или давно не читался, первая загрузка может идти через реальный диск, а повторная — почти мгновенно из page cache. Разница между "холодным" и "тёплым" запросом иногда сама по себе объясняет весь разрыв с iperf3.
Веб-сервер и прокси: лимиты, которые вы сами когда-то поставили
Следующий частый источник расхождения — сам веб-сервер или прокси перед ним. В отличие от iperf3, который отдаёт данные из памяти без всякой логики поверх, HTTP-сервер проходит через слой конфигурации, где вполне могут быть искусственные ограничения:
- Лимит скорости отдачи. В nginx это директива
limit_rate(иlimit_rate_afterдля первых N байт без ограничения) — часто её ставят "на будущее" для защиты от злоупотреблений и забывают. Проверить:grep -r "limit_rate" /etc/nginx/. - Лимит частоты запросов.
limit_reqрежет не скорость одного соединения, а число запросов в единицу времени — но при повторных закачках или параллельных запросах может создавать впечатление "медленной сети". - Число воркеров и соединений.
worker_processes,worker_connections— при нехватке воркеров под нагрузкой сервер может обслуживать запросы с задержками, что выглядит как низкая скорость, хотя канал ни при чём. - Прокси/CDN/WAF перед сервером. Если между клиентом и вашим бэкендом есть промежуточный слой (CDN, обратный прокси у другого провайдера, панель управления со встроенным reverse-proxy), у него может быть собственный "fair use" лимит скорости, никак не связанный ни с вашим каналом, ни с диском.
Проверить последний пункт проще всего, обратившись напрямую к порту приложения или бэкенда в обход внешнего прокси (если это возможно в вашей топологии) и сравнив скорость. Если напрямую быстрее — лимит стоит на промежуточном слое, и его нужно искать не в конфиге собственного nginx, а в настройках того, что стоит перед ним.
TLS и другие накладные расходы одиночного соединения
TLS-рукопожатие добавляет один-два круговых обхода (round-trip) до того, как пойдут первые байты полезных данных — это заметно на маленьких файлах и множестве коротких запросов, но для одной большой закачки, после того как соединение установлено, шифрование само по себе редко становится узким местом: современные CPU с аппаратным ускорением AES обрабатывают шифрованный трафик практически на скорости сетевой карты.
Тем не менее TLS стоит проверить отдельным пунктом, особенно если сервер физически далеко от клиента, а RTT и так высокий — тогда задержка на рукопожатие складывается с той же проблемой TCP-окна, о которой шла речь выше, и субъективно "тормозит всё". Насколько дорого обходится TLS-рукопожатие именно из удалённого региона и какие цифры считать нормой — разобрано в статье про цену TLS-рукопожатия из далёкого региона.
Практическая проверка простая — сравнить HTTP и HTTPS на одном и том же файле (если у вас есть возможность временно поднять HTTP-раздачу того же файла для теста):
curl -o /dev/null -s -w "connect: %{time_connect}s tls: %{time_appconnect}s ttfb: %{time_starttransfer}s total: %{time_total}s speed: %{speed_download} bytes/s\n" \
https://example.com/big-file.bin
Если time_appconnect (время до завершения TLS-рукопожатия) — небольшая доля от time_total, а основная масса времени уходит уже после первого байта, TLS можно вычеркнуть из списка подозреваемых и сосредоточиться на диске, TCP-окне или лимитах прокси.
Пошаговый план диагностики
Когда цифры iperf3 и реальной загрузки расходятся, имеет смысл пройти по пунктам в таком порядке — от самого дешёвого теста к самому дорогому:
- Честное сравнение iperf3. Повторите тест с
-P 1между теми же клиентом и сервером, что и в HTTP-тесте. Сравните с-P 4или-P 8— если разница большая, вы уже нашли причину: одиночный поток упирается в окно, а не в канал. - Скорость диска отдельно от сети. Замерьте чтение файла локально на сервере через
ddилиfio, без участия сети вообще. Если цифра близка к скорости HTTP-загрузки — узкое место в дисковой подсистеме, а не в канале. - Разбивка времени HTTP-запроса.
curl -wс таймингами (time_connect,time_appconnect,time_starttransfer,time_total) покажет, где именно уходит время — на установку соединения, на TLS или уже на передачу тела ответа. - HTTP против HTTPS. Если можете временно раздать тот же файл без TLS — сравните напрямую, чтобы исключить или подтвердить накладные расходы шифрования.
- В обход прокси. Если топология позволяет, обратитесь напрямую к бэкенду или приложению, минуя nginx/CDN/WAF, и сравните скорость.
- Конфиг веб-сервера. Проверьте
limit_rate,limit_req, число воркеров и активных соединений — не тот случай не забыт ли где-то давний лимит. - Фоновая нагрузка. Убедитесь, что во время замера на сервере не идёт бэкап, другая закачка или процесс, конкурирующий за диск или сеть — иначе результаты замеров будут "плавать" от прогона к прогону.
Если после всех пунктов расхождение сохраняется, а канал явно не насыщен другим трафиком — стоит проверить сетевые лимиты на уровне самой виртуальной машины: некоторые провайдеры режут не по мегабитам, а по пакетам в секунду (pps), и тогда узкое место найдётся уже не в TCP-окне и не в диске, а в лимитах виртуалки — это отдельная и довольно частая история, разобранная в статье про сетевые лимиты виртуалки, где pps важнее гигабит.
Для наглядности — сводная таблица различий между двумя тестами:
| Параметр | iperf3 (обычный запуск) | Одиночная HTTP/HTTPS-загрузка |
|---|---|---|
| Число TCP-потоков | часто несколько (-P N) | как правило, один |
| Источник данных | генерируется в памяти | читается с диска / из page cache |
| Ограничение TCP-окна/BDP | размывается суммой потоков | видно напрямую, особенно при высоком RTT |
| Веб-сервер и его лимиты | не участвует | nginx/apache, возможные limit_rate, лимиты воркеров |
| TLS | обычно отсутствует | есть, если по HTTPS |
| Что показывает | ёмкость канала между двумя точками | реальную скорость конкретного сценария отдачи |
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
iperf3 показывает почти гигабит, а браузер при скачивании файла — вдвое меньше. Это норма?
Да, если iperf3 запускался с несколькими параллельными потоками (-P больше единицы) или тест шёл в условиях, отличных от реальной загрузки (другой маршрут, другой RTT). Для честного сравнения гоняйте iperf3 -P 1 и сравнивайте именно с ним.
Можно ли ускорить одиночную HTTP-загрузку, если дело в TCP-окне, а не в диске или лимитах?
Иногда да — тюнингом размеров окна на сервере и клиенте (net.ipv4.tcp_rmem, net.ipv4.tcp_wmem) или переходом на параллельную загрузку в несколько соединений (менеджер закачек, Range-запросы). Подробности — в статье про TCP-окно, ссылка выше.
Как быстро понять, что дело именно в диске, а не в сети?
Замерить чтение файла локально на сервере через dd или fio, вообще без участия сети. Если локальная скорость чтения близка к скорости, которую вы видите при скачивании — это диск.
Стоит ли качать файлы в несколько параллельных потоков, если канал явно не насыщается одним соединением?
Да, это законный и рабочий способ обойти потолок одного TCP-потока — тот же принцип, что использует сам iperf3 с -P N. Для больших файлов через менеджеры закачек или клиенты с поддержкой Range-запросов это часто даёт кратный прирост без каких-либо изменений на сервере.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →