Сеть против диска
В пятницу файл на NFS-шаре «повесил» сервис, хотя с сетью тот работал без нареканий. Инцидент стал первым, где сеть и диск проявили себя как разные дороги.
сеть работает через poller и не блокирует потоки, а дисковый I/O — это честный блокирующий syscall, который реально держит поток в ядре.
Два разных пути ожидания
Сетевое чтение, которое должно бы блокироваться, не вызывает блокирующий syscall: горутина паркуется, дескриптор регистрируется в network poller, и M освобождается немедленно. Никакого потока в ядре не застревает.
Дисковый ввод-вывод устроен иначе. Чтение файла — это блокирующий syscall: горутина и её поток уходят в ядро вместе и возвращаются вместе. Пока ядро ждёт диск, поток занят.
Оговорка: через poller не ходят именно регулярные файлы; нерегулярные (FIFO, терминалы, сокеты) могут идти через него. Для обычных файлов картина как выше.
Что это значит для кода
Отсюда практическое правило: сетевые блокировки «бесплатны» (не тратят M), а дисковые — дороже, потому что реально держат поток. Сервис с десятью тысячами сетевых соединений — норма. Сервис с десятью тысячами одновременных дисковых чтений — уже история про исчерпание потоков (урок).
conn.Read(buf) // сеть: парковка, поток свободен
file.Read(buf) // диск: поток уходит в ядро
Практическое следствие
Именно поэтому дисковый I/O под нагрузкой ограничивают — семафорами и воркер-пулами (уроки,), а сетевой — не принято. И поэтому же context с таймаутами критичен для сетевых вызовов: горутина может «ждать сеть» вечно, если никто не отменит (урок).
Вы узнаете:
- почему диск не встроили в poller
- как
entersyscallработает для диска
Проверьте себя
Сервис с 100 000 одновременных вебсокетов — нужно ли лимитировать горутины?
Почему таймауты критичны именно для сетевых вызовов, а не для дисковых?
file.Read паркует горутину и освобождает поток?
правильных ответов
Сеть — poller, диск — блокирующий syscall. Один поток на тысячу сетевых ожиданий, но один поток на каждое дисковое. Отсюда — разная инженерная стратегия.
10 000 ₽ · 60 минут · онлайн