netpoll
Netpoll Go — это основной механизм мультиплексирования ввода-вывода, реализованный средой выполнения Go. Он позволяет Go реализовывать высокопроизводительные сетевые службы с лаконичной синхронной моделью программирования (одна Goroutine на соединение). По сути, это глубокая интеграция системных вызовов мультиплексирования ввода-вывода на уровне операционной системы (таких как epoll в Linux) с планировщиком Goroutine в Go.
Сравнение между Netpoll и собственной сетевой библиотекой Go
1. Количество Goroutines
- Go Net: Одна Goroutine на соединение.
- NetPoll: Несколько соединений под одной Goroutine.
2. Переключение контекста Goroutine/Давление планирования
- Go Net: Высокое давление переключения при высокой конкурентности. Поскольку Go Net имеет одну Goroutine на соединение, происходит частое переключение при большом количестве goroutine.
- NetPoll: Низкое давление переключения при высокой конкурентности. Поскольку NetPoll разделяет одну goroutine между несколькими соединениями, переключений меньше.
3. Потребление памяти
- Go Net: Потребление памяти положительно коррелирует с количеством соединений. Высокая конкурентность может вызвать давление памяти из-за взрыва Goroutine.
- NetPoll: Предварительно выделяет буферы и создаёт пул буферов.
4. Режим триггера
- Go Net: Использует Edge Triggered (ET). Считывает все данные за раз, простая реализация.
- NetPoll: Использует Level Triggered (LT). Не нужно предварительно выделять большую память как буферы данных.
5. Поддержка совместного использования пула буферов между ядром и пользовательским уровнем (на одну копию меньше)
- Go Net: Не поддерживается.
- NetPoll: Поддерживается. Управляет пулом буферов, который напрямую передаётся пользователям, требуя на одну копию меньше.
6. Применимые сценарии
- Go Net: Низкая конкурентность, простые сценарии.
- NetPoll: Высокая конкурентность, сценарии с низкой задержкой.
Концепции дизайна Netpoll
1. Базовая архитектура
Собственная сетевая библиотека разработана на основе режима epoll LT. Базовая архитектура показана на рисунке ниже:

2. Использование Goroutines
Существует пул объектов poll. Каждый объект poll имеет epoll и отдельно связан с goroutine. Количество goroutine согласовано с количеством объектов poll, и каждый объект poll может прослушивать несколько файловых дескрипторов (fd).
3. Логика чтения/записи I/O
- Каждый объект poll запускает goroutine для непрерывного опроса читаемых/записываемых событий на текущем epoll.
- Каждый epoll связан с несколькими fd, и каждый fd связан с Buffer.
- Когда слышно читаемое событие от fd, данные читаются в Buffer.
- Непрерывно опрашивает и обрабатывает данные в Buffer. Когда чтение данных завершено, уведомляет пул goroutine последующей логики обработки GoPoll для выполнения.
- Взаимодействие с ядром через системные вызовы полностью контролируется netpoll. Чтения и записи пользовательского уровня в Conn просто оперируют общим Buffer.
4. Преимущества и недостатки
Преимущества: Более сильная поддержка возможностей высокой конкурентности.
- Опрос обрабатывает данные в Buffer, поэтому нет ситуации, когда данные не обрабатываются.
- Одна goroutine соответствует нескольким соединениям. Даже при большом количестве соединений накладные расходы планирования и переключения ресурсов не значительны.
- Поддерживает совместное использование буфера между пользователем и ядром, уменьшая одну операцию копирования данных и повышая эффективность.
Недостатки: Требуется больше памяти.
