Как контролировать доступное пространство Linux-буфера UDP? - PullRequest
42 голосов
/ 18 февраля 2010

У меня есть приложение Java на Linux, которое открывает сокет UDP и ждет сообщений.

Через пару часов при большой нагрузке происходит потеря пакетов, то есть пакеты принимаются ядром, а не моим приложением (мы видим потерянные пакеты в сниффере, мы видим потерянные пакеты UDP в netstat, мы посмотрите эти пакеты в журналах нашего приложения).

Мы попытались увеличить буферы сокетов, но это не помогло - мы начали терять пакеты позже, но это все.

Для отладки я хочу знать, насколько заполнен буфер udp ОС в любой момент времени. Погуглил, но ничего не нашел. Вы можете мне помочь?

P.S. Ребята, я в курсе, что UDP ненадежен. Однако - мой компьютер получает все UDP-сообщения, в то время как мое приложение не может использовать некоторые из них. Я хочу максимально оптимизировать свое приложение, вот причина вопроса. Благодарю.

Ответы [ 4 ]

56 голосов
/ 20 августа 2013

UDP является вполне жизнеспособным протоколом. Это тот же самый старый случай правильного инструмента для правильной работы!

Если у вас есть программа, которая ожидает дейтаграммы UDP, а затем отключается, чтобы обработать их, прежде чем вернуться к ожиданию другой, то истекшее время обработки должно всегда быть быстрее, чем скорость поступления дейтаграмм в худшем случае. Если это не так, очередь приема сокета UDP начнет заполняться.

Это может быть допустимо для коротких очередей. Очередь делает именно то, что должна делать - ставит дейтаграммы в очередь, пока вы не будете готовы. Но если средняя скорость поступления регулярно вызывает отставание в очереди, пришло время изменить дизайн вашей программы. Здесь есть два основных варианта: сократить время обработки с помощью хитроумных методов программирования и / или многопоточности вашей программы. Также может использоваться балансировка нагрузки для нескольких экземпляров вашей программы.

Как уже упоминалось, в Linux вы можете проверить файловую систему proc, чтобы узнать, что такое UDP. Например, если я cat узел /proc/net/udp, я получаю что-то вроде этого:

$ cat /proc/net/udp   
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode ref pointer drops             
  40: 00000000:0202 00000000:0000 07 00000000:00000000 00:00000000 00000000     0        0 3466 2 ffff88013abc8340 0           
  67: 00000000:231D 00000000:0000 07 00000000:0001E4C8 00:00000000 00000000  1006        0 16940862 2 ffff88013abc9040 2237    
 122: 00000000:30D4 00000000:0000 07 00000000:00000000 00:00000000 00000000  1006        0 912865 2 ffff88013abc8d00 0         

Исходя из этого, я вижу, что сокет, принадлежащий идентификатору пользователя 1006, прослушивает порт 0x231D (8989) и что очередь на получение составляет около 128 КБ. Поскольку максимальный размер моей системы составляет 128 КБ, это говорит о том, что моя программа крайне слаба в соответствии с поступающими датаграммами. Пока было отброшено 2237, то есть уровень UDP не может помещать больше дейтаграмм в очередь сокетов и должен их отбрасывать.

Вы можете наблюдать за поведением вашей программы с течением времени, например, с помощью:

watch -d 'cat /proc/net/udp|grep 00000000:231D'

Обратите внимание, что команда netstat делает то же самое: netstat -c --udp -an

Мое решение для моей программы weenie будет многопоточным.

Ура!

35 голосов
/ 18 февраля 2010

Linux предоставляет файлы /proc/net/udp и /proc/net/udp6, в которых перечислены все открытые сокеты UDP (для IPv4 и IPv6 соответственно). В обоих столбцах tx_queue и rx_queue отображаются исходящие и входящие очереди в байтах.

Если все работает должным образом, вы обычно не увидите никаких значений, отличных от нуля, в этих двух столбцах: как только ваше приложение генерирует пакеты, они отправляются через сеть, и как только эти пакеты приходят из сети, ваше приложение проснется и получит их (вызов recv немедленно возвращается). Вы можете увидеть повышение rx_queue, если ваше приложение имеет открытый сокет, но не вызывает recv для получения данных, или если оно не обрабатывает такие данные достаточно быстро.

4 голосов
/ 30 августа 2012

rx_queue сообщит вам длину очереди в любой данный момент, но не сообщит вам, насколько заполнена очередь, то есть отметка верхнего уровня.Нет способа постоянно контролировать это значение, и нет способа получить его программно (см. Как получить объем данных в очереди для сокета UDP? ).

Единственный способ, которым я могуПредставьте себе, что отслеживание длины очереди - это перемещение очереди в вашу собственную программу.Другими словами, запустите два потока - один читает сокет как можно быстрее и выгружает дейтаграммы в вашу очередь;а другая - ваша программа, извлекающая из этой очереди и обрабатывающая пакеты.Это, конечно, предполагает, что вы можете убедиться, что каждый поток находится на отдельном процессоре.Теперь вы можете отслеживать длину своей очереди и отслеживать отметку на высокой воде.

0 голосов
/ 15 сентября 2011

Процесс прост:

  1. При желании приостановить процесс подачи заявления.

  2. Откройте сокет UDP. Вы можете получить его из запущенного процесса, используя /proc/<PID>/fd, если это необходимо. Или вы можете добавить этот код в само приложение и отправить ему сигнал - конечно, сокет уже будет открыт.

  3. Позвоните recvmsg в замкнутом цикле как можно быстрее.

  4. Подсчитайте, сколько пакетов / байтов вы получили.

Это приведет к удалению любых дейтаграмм, находящихся в данный момент в буфере, но если это нарушит ваше приложение, ваше приложение уже будет сломано.

...