UDP-пакет отбрасывается ядром Linux
У меня есть сервер, который отправляет UDP-пакеты через многоадресную рассылку, и несколько клиентов, которые перечисляют эти многоадресные пакеты. Каждый пакет имеет фиксированный размер 1040 байт, общий размер данных, отправляемых сервером, составляет 3 ГБ.
Моя среда выглядит следующим образом:
Сеть Ethernet 1 Гбит
40 узлов, 1 отправитель и 39 получателей. Все узлы имеют одинаковую аппаратную конфигурацию: 2 процессора AMD, каждый процессор имеет 2 ядра на частоте 2,6 ГГц
На стороне клиента один поток читает сокет и помещает данные в очередь. Один дополнительный поток извлекает данные из очереди и выполняет небольшую обработку.
Во время многоадресной передачи я распознаю частоту отбрасывания пакетов 30% на стороне узла. Наблюдая статистику netstat –su, я могу сказать, что пропущенные клиентским приложением пакеты равны значению RcvbufErrors из вывода netstat.
Это означает, что все отсутствующие пакеты отбрасываются ОС, потому что буфер сокета был заполнен, но я не понимаю, почему поток захвата не может прочитать буфер вовремя. Во время передачи 2 из 4 ядер используются на 75%, остальные спят. Я единственный, кто использует эти узлы, и я предположил бы, что у машин такого типа нет проблем с пропускной способностью 1 Гбит. Я уже провел некоторую оптимизацию, добавив флаги компилятора g++ для amd cpus, это уменьшило частоту выпадения пакетов до 10%, но, на мой взгляд, все еще слишком высоко.
Конечно, я знаю, что UDP не надежен, у меня есть свой собственный протокол исправления.
У меня нет никаких прав администратора, поэтому я не могу изменить параметры системы.
Любые советы, как я могу увеличить производительность?
РЕДАКТИРОВАТЬ: я решил эту проблему с помощью 2 потоков, которые читают сокет. Буфер сокета recv все еще иногда переполняется. Но среднее падение составляет менее 1%, поэтому с ним не проблема.
3 ответа
Отслеживание сбрасываний сети в Linux может быть немного сложным, поскольку существует много компонентов, в которых могут происходить отбрасывания пакетов. Они могут возникать на аппаратном уровне, в подсистеме сетевых устройств или на уровне протоколов.
Я написал очень подробное сообщение в блоге, объясняющее, как контролировать и настраивать каждый компонент. Здесь довольно сложно кратко изложить краткий ответ, поскольку существует так много различных компонентов, которые необходимо отслеживать и настраивать.
Помимо очевидного удаления всего несущественного из цикла чтения сокета:
- Увеличьте приемный буфер сокета с
setsockopt(2)
, - использование
recvmmsg(2)
если ваше ядро его поддерживает, чтобы уменьшить количество системных вызовов и копий ядра пользователя, - Рассмотрим неблокирующий подход с триггером
epoll(7)
, - Посмотрите, действительно ли вам нужны потоки, блокировка / синхронизация очень дороги.
"На стороне клиента один поток читает сокет и помещает данные в очередь. " Я думаю, проблема в этом потоке. Он не получает сообщения достаточно быстро. Слишком много времени уходит на что-то другое, например, на получение мьютекса при помещении данных в очередь. Попробуйте оптимизировать операции в очереди, например использовать очередь без блокировки.