Несколько клиентов, отправляющих UDP-данные в python-сокет, теряются - PullRequest
0 голосов
/ 02 мая 2018

У меня есть читатель Python для чтения входящих пакетов UDP от 5000 клиентов каждую минуту. Когда я начал развертывать его, он работал нормально, но теперь, когда у меня до 4000 клиентов, я теряю около 50% поступающих данных. Виртуальная машина имеет много памяти и процессора, поэтому я предполагаю, что это что-то с моим UDP слушатель сокета на сервере, получающий слишком много данных одновременно. Через cron клиенты каждую минуту отправляют эти данные:

site8385','10.255.255.255','1525215422','3.3.0-2','Jackel','00:15:65:20:39:10'

Это часть читателя сокета моего скрипта слушателя.

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
port = 18000
s.bind(('', port))

while True:
   # Establish connection with client.

   d = s.recvfrom(1024)

Может быть, размер буфера слишком мал? Как определить размер входящих пакетов, чтобы я мог настроить значение 1024?

1 Ответ

0 голосов
/ 02 мая 2018

Каждые 60 секунд вы получаете шторм из ~ 5000 сообщений. Вы обрабатываете их последовательно, и это занимает «совсем немного» времени. Очень быстро один из ваших буферов переполняется, и ваша ОС, сетевая карта или маршрутизатор начинают сбрасывать пакеты. (Скорее всего, это буфер, который ваше ядро ​​выделяет для этого конкретного сокета, и ядро ​​отбрасывает пакеты, но возможны и все остальные параметры.)

Вы можете попробовать увеличить эти буферы. Это даст вам гораздо больше «разрешенного времени задержки», так что вы можете отстать еще до того, как ядро ​​начнет отбрасывать пакеты. Если вы хотите пойти по этому пути, первым шагом будет setsockopt, чтобы поднять значение SO_RCVBUF, но вам действительно нужно узнать обо всех проблемах, которые могут возникнуть здесь. 1

Если вы управляете клиентским кодом, вы также можете заставить клиентов разбивать свои пакеты (например, просто спать в течение random.random() * 55 перед send).

Но, вероятно, лучше попытаться на самом деле обслуживать эти пакеты как можно быстрее и выполнять обработку в фоновом режиме. 2

Попытка сделать это в потоке может быть идеальной, но это также может быть очень рискованным, чтобы получить право. Более простое решение - просто фоновый поток или их пул:

def process_msg(d):
    # your actual processing code

with concurrent.futures.ThreadPoolExecutor(max_workers=12) as x:
    while True:
        d = s.recvfrom(1024)
        x.submit(process_msg, d)

Это может на самом деле не помочь. Если ваша обработка связана с процессором, а не с вводом / выводом, фоновые потоки будут просто бороться за GIL с основным потоком. Если вы используете Python 2.7 или 3.2 или что-то еще старое, даже потоки, связанные с вводом / выводом, могут вмешиваться в некоторых ситуациях. Но в любом случае, есть простое решение: просто замените это ThreadPoolExecutor на ProcessPoolExecutor (и, возможно, уменьшите max_workers на 1 число ядер, которое у вас есть, чтобы убедиться, что принимающий код может иметь целое ядро ​​для сам по себе).


1. Redhat имеет хороший документ по Настройка производительности сети . Он написан больше с точки зрения системного администратора, чем с точки зрения программиста, и ожидает, что вы либо узнаете, либо узнаете, как искать, много дополнительной информации, но это должно быть полезно, если вы захотите это сделать. Вы также можете попробовать поискать ошибки сервера, а не переполнение стека, если хотите пойти по этому пути.

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

...