Потерянные пакеты UDP (JBoss + DatagramSocket)

Я разрабатываю часть корпоративного приложения на основе JBoss+EJB. Моему модулю необходимо обрабатывать огромное количество входящих UDP-пакетов. Я провел нагрузочное тестирование, и оказалось, что при отправке пакетов с интервалом 11 мс все нормально, но при интервале 10 мс некоторые пакеты теряются. На мой взгляд, это довольно странно, но я несколько раз сравнивал нагрузочные тесты с интервалом 10/11 мс, и результат всегда один и тот же (10 мс - какие-то "потерянные" пакеты, 11 мс - все нормально).

Если что-то было не так с синхронизацией, я ожидаю, что это также будет видно в случае 11-мс тестов (по крайней мере, один потерянный пакет или хотя бы одно неправильное значение счетчика). Так что, если это не из-за синхронизации, то, возможно, DatagramSocket, через который я получаю пакеты, не работает должным образом.

Я обнаружил, что размер приемного буфера (SO_RCVBUF) имеет значение по умолчанию 57344 (вероятно, это зависит от базовых сетевых буферов ввода-вывода). Я подозреваю, что, возможно, когда этот буфер заполняется, новые входящие дейтаграммы UDP отклоняются. Я попытался установить это значение немного выше, но заметил, что если я преувеличиваю, буфер возвращается к размеру по умолчанию. Если это зависит от базового уровня, как я могу узнать максимальный размер буфера для определенной ОС/сетевой карты на уровне JBoss?

Возможно ли, что это вызвано размером приемного буфера, или, может быть, значение 57344 достаточно велико для обработки большинства случаев? Есть ли у вас опыт решения таких вопросов?

В моем DatagramSocket не установлен тайм-аут. Мои дейтаграммы UDP содержат около 70 байт данных (значение без заголовка дейтаграммы).

[Отредактировано] Мне приходится использовать UDP, потому что я получаю данные Cisco Netflow — это протокол, используемый сетевыми устройствами для отправки некоторой статистики трафика. Кроме того, я не имею никакого влияния на формат отправляемых байтов (например, я не могу добавлять счетчики для пакетов и т. д.). Не ожидается, что все пакеты будут обработаны (некоторые дейтаграммы могут быть потеряны), но я ожидаю, что обработаю большую часть пакетов. Во время тестов с интервалом 10 мс было потеряно около 30% пакетов.

Маловероятно, что медленная обработка вызывает эту проблему. В настоящее время компонент singleton содержит ссылку на DatagramSocket, вызывающий метод получения в цикле. Когда пакет получен, он передается в очередь и обрабатывается выбранным из пула компонентом без сохранения состояния. «Фасадный» синглтон отвечает только за получение пакетов и передачу их на обработку (он не ждет завершения обработки).

Заранее спасибо, Петр


person omnomnom    schedule 23.02.2011    source источник
comment
Зачем нужен УДП? Я бы использовал TCP до тех пор, пока у вас не будет профилирования, указывающего на насыщенный уровень с необходимостью перехода на UDP. Кроме того, мой опыт работы с UDP заключается в том, что данные обычно дублируются. Вот текущее состояние. Так что не волнуйтесь, если вы пропустите этот пакет, потому что скоро появятся другие пакеты!   -  person corsiKa    schedule 23.02.2011
comment
Маловероятно, что медленная обработка вызывает эту проблему. -- Я думаю, что да, потому что 10 мс мало, примерно такая же продолжительность, как квант потока.   -  person ChrisW    schedule 23.02.2011
comment
Эти 10 мс могут различаться в разных операционных системах/процессорах. Можете ли вы отправлять данные реже, скажем, 100 мс?   -  person Jeff Storey    schedule 23.02.2011
comment
@ChrisW Возможно, вы правы, но если так, то не должны ли потери пакетов наблюдаться также в случае интервала 11 мс? (Я не могу ничего наблюдать) @Jeff Storey Насколько быстро будут отправляться пакеты, зависит только от внешних сетевых устройств. Я отвечаю только за получение и обработку.   -  person omnomnom    schedule 23.02.2011


Ответы (4)


UDP по своей сути ненадежен.

Дейтаграммы могут быть отброшены в любой точке между отправителем и получателем, даже внутри получателя на уровне ниже вашего кода. Установка буфера recv на больший размер, вероятно, поможет сетевому коду на вашем компьютере буферизовать больше дейтаграмм, но вы должны ожидать, что некоторые дейтаграммы все равно будут потеряны.

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

[Повторите ваше редактирование...] А что обрабатывает вашу очередь и как работает блокировка между производителем и потребителями? Измените свой код так, чтобы логика recv просто увеличивала счетчик, отбрасывала данные и возвращалась назад, чтобы посмотреть, теряете ли вы меньше дейтаграмм; в любом случае, UDP ненадежен, у вас будут дейтаграммы, которые будут отброшены, и вы должны просто ожидать этого и иметь дело с этим. Беспокойство об этом означает, что вы фокусируетесь не на той проблеме; используйте данные, которые вы ДЕЙСТВИТЕЛЬНО получаете, и предполагайте, что вы не получите большую их часть, и тогда ваша программа будет работать, даже если сеть будет перегружена и БОЛЬШИНСТВО ваших дейтаграмм будет отброшено.

Таким образом, именно так обстоит дело с UDP.

person Len Holgate    schedule 23.02.2011
comment
Спасибо за ответ. Производитель содержит ссылку на DatagramSocket, а производителем является JBossEJB3ext @Service bean (синглтон). Он вызывает метод блокировки цикла while(true) для получения. Когда он получает пакет, DatagramSocket передается в один из объединенных в пул bean-компонентов без сохранения состояния через AsyncUtils (асинхронный вызов). Попробую отключить обработку и считать только входящие пакеты, потом дам отзыв. - person omnomnom; 24.02.2011

UDP не гарантирует доставку, поэтому вы можете настроить параметры, но вы не можете гарантировать, что сообщение будет доставлено, особенно в случае передачи очень больших объемов данных.

Если вам нужно гарантировать доставку, вы должны вместо этого использовать TCP.

Если вам нужно (или вы хотите) использовать UDP, вы можете закодировать каждый пакет числом, а также отправить ожидаемое количество пакетов. Например, если вы отправили 10 больших пакетов, вы можете включить информацию: пакет 1/10, пакет 2/10 и т. д. Таким образом вы сможете, по крайней мере, определить, получили ли вы не все пакеты. Если вы их не получили, вы можете отправить запрос на повторную отправку этих отсутствующих пакетов.

person Jeff Storey    schedule 23.02.2011
comment
Да, как только у вас будут большие передачи, пакет будет идти сверх MTU. В этом случае пакет становится фрагментированным. При использовании UDP не гарантируется даже получение фрагментов по порядку, но базовый протокол (IP) отбрасывает пакет, если это не так, поэтому UDP никогда не получит пакет сам по себе. Вот почему большие пакеты с UDP гораздо менее надежны — за кулисами происходит больше отбрасывания, поскольку UDP сохраняет границы :) - person Chris Dennett; 23.02.2011
comment
Спасибо за ответы, к сожалению, я не влияю на то, как генерируются пакеты (также я не могу переключиться на TCP) - пожалуйста, взгляните на мой отредактированный пост. - person omnomnom; 23.02.2011

В ваших тестах видно, что в буфере может находиться только до двух пакетов, поэтому, если размер каждого пакета меньше 28 КБ, все должно быть в порядке.

Как вы знаете, UDP работает с потерями, но вы должны иметь возможность отправлять более одного пакета за 10 мс. Я предлагаю вам написать простой приемник, который просто прослушивает пакеты, чтобы определить, является ли это вашим приложением или чем-то еще на уровне сети/ОС. (подозреваю, что позднее)

person Peter Lawrey    schedule 23.02.2011
comment
есть ли способ узнать о переполнении буфера. Потому что я получаю пакеты постоянно, и после долгого времени работы они перестают приниматься. Это из-за переполнения буфера. - person George Thomas; 02.09.2016
comment
@GeorgeThomas получатель понятия не имеет, теряются ли пакеты и почему. Определить это можно только опытным путем. например поиграйте с размером или расстоянием между пакетами. - person Peter Lawrey; 02.09.2016
comment
Я понятия не имею! Я реализовал прослушиватель udp в Android, и я получаю пакет каждые 1 секунду. Все работает нормально, но через какое-то время полностью перестает. Я постараюсь изменить размер и расстояние и попробовать - person George Thomas; 02.09.2016
comment
@GeorgeThomas Возможно, вам придется проверить это на том месте, где вы можете отслеживать пакеты UDP. например с помощью WireShark. Нет веской причины, по которой UDP-пакеты должны просто останавливаться. Вы должны видеть только случайные пакеты, потерянные под нагрузкой. - person Peter Lawrey; 02.09.2016
comment
Что происходит, когда вы пытаетесь повторно подключиться? Что происходит, например, при многократном подключении к разным портам? Они все останавливаются сразу или только одна останавливается? - person Peter Lawrey; 02.09.2016
comment
когда я убиваю приложение и снова открываю приложение, оно снова работает. Есть ли способ узнать, что соединение потеряно, и восстановить соединение? На разные порты не пробовал. - person George Thomas; 02.09.2016
comment
попробую вайршарк - person George Thomas; 02.09.2016
comment
@GeorgeThomas UDP не имеет понятия о соединении, поэтому нет ничего, что могло бы обнаружить, что вы не получаете пакеты. Что вы можете сделать, так это добавить пульс, и если вы не получаете никаких пакетов в течение определенного периода времени, предположите, что есть проблема. Что вам следует делать, зависит от того, почему это не удалось. - person Peter Lawrey; 02.09.2016
comment
@GeorgeThomas Wireshark поможет вам определить, прекратил ли ваш сервер отправку пакетов из-за ошибки на сервере (что, как я подозреваю, весьма вероятно). пакетов, так как вы не получите уведомление о том, что оно пропало. - person Peter Lawrey; 02.09.2016
comment
Я думаю, что сервер все еще отправляет, потому что приложение ios успешно получает пакеты, поэтому я не думаю, что это проблема с сервером. - person George Thomas; 02.09.2016
comment
Да, мне нужно будет определить, когда сервер может прекратить отправку пакетов при попытке переподключения. - person George Thomas; 02.09.2016

Я не знаю Java, но... позволяет ли API вызывать асинхронное прослушивание/получение дейтаграммы:

  • Используйте API O/S для получения (передавая буфер уровня приложения в качестве параметра)
  • (Подождите, пока нечего получать...)
  • (O/S получает что-то из сети...)
  • O/S помещает полученный пакет в буфер и завершает/возвращает ваш вызов API

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

person ChrisW    schedule 23.02.2011
comment
Спасибо за ответ. В настоящее время используется одноэлементный фасад — он содержит ссылку на DatagramSocket. Он вызывает метод receive() в цикле - это блокирующая операция. Если дейтаграмма получена, то она передается компоненту обработки (объединенные bean-компоненты без сохранения состояния). Контейнер управляет синхронизацией. Такой подход, на мой взгляд, защищает от проблем с синхронизацией, а также обеспечивает масштабируемость. Разрешен только один экземпляр DatagramSocket (привязанный к IP-адресу и порту). - person omnomnom; 23.02.2011
comment
@Piotrek Я предлагаю вам попробовать изменить это: вместо одной синхронной/блокирующей операции попробуйте выполнить несколько асинхронных/неблокирующих операций... чтобы ОС имела несколько параллельных буферов уровня приложения, в которые можно было бы принимать несколько пакетов. . - person ChrisW; 23.02.2011