Несколько клиентов, отправляющих данные UDP в сокет Python, теряются

У меня есть считыватель сокетов 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?

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

user207421 02.05.2018 01:36

Это 5000 / минута около 83 / секунда +/- несколько сотен% или около того, или это шторм, когда почти все 5000 приходят сразу, а затем ничего не происходит в течение 59 секунд?

abarnert 02.05.2018 01:57

@abarnert Да. Все приходят сразу, то 59 секунд ничего не происходит. Байт составляет около 80 на одного клиента.

teamg 02.05.2018 02:30

Количество байтов не должно быть проблемой (если только вы не используете канал со скоростью 10 Мбит / с, чего, вероятно, нет). Но количество пакетов в секунду могло быть. Сколько работы вы делаете после этого d.recvfrom?

abarnert 02.05.2018 02:34

@abarnert Совсем немного. Каждая запись записывается в базу данных mariadb.

teamg 02.05.2018 02:36

Хорошо, есть два способа атаковать это: (1) попробуйте увеличить размер приемного буфера, чтобы он не начинал отбрасывать пакеты, пока вы не отстанете намного дальше, или (2) попытайтесь обслужить буфер как можно быстрее, просто просто отправка всех сообщений в какой-то другой канал (скажем, multiprocessing.Queue, обслуживаемый multiprocessing.Process - или, что проще, просто бросать задачи в multiprocessing.Pool или concurrent.futures.ProcessPoolExecutor).

abarnert 02.05.2018 02:40

@abarnert Увеличил буфер до 64 КБ, но он обрабатывается примерно так же, как когда он был установлен на 80 КБ. Я также закомментировал записи sql, но это тоже не повлияло.

teamg 02.05.2018 02:50

Я не уверен, какой буфер вы увеличили, но это не ускорит процесс; это просто означает, что ядро ​​дает вам больше времени, прежде чем отбрасывать пакеты (при условии, что это происходит в вашем ядре, а не в вашей сетевой карте, вашем маршрутизаторе, вашем восходящем провайдере и т. д.).

abarnert 02.05.2018 02:52

Между тем, если вы закомментируете все работу, а не только запись SQL, будет ли это иметь какое-то значение?

abarnert 02.05.2018 02:52

@abarnert Комментирование всей работы очень помогло, и почти все они получили. Когда я говорю, что изменил размер буфера, я имею в виду эту строку: d = s.recvfrom (1024)

teamg 02.05.2018 03:12

О, размер буфера что вообще не имеет значения. Вы всегда будете получать одно сообщение, и если это обычно около 80 байт, не имеет значения, насколько больше 80 вы установите максимум. Я имел в виду использование setsockopt для увеличения приемного буфера сокета и, возможно, использование sysctl для увеличения максимального приемного буфера сокета, чтобы вы могли увеличить его еще больше. Посмотрите SO_RCVBUF на своей платформе для получения подробной информации (он должен быть в man 7 socket), и несколько других sockopts, а также упомянутые там значения файловой системы sysctl или /proc. (Или, держу пари, есть хороший вопрос по ServerFault.)

abarnert 02.05.2018 03:20

@abarnert Так что это определенно sql пишет, что замедляет его. Позвольте мне выбрать варианты, которые вы предоставили. Спасибо.

teamg 02.05.2018 03:56
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
12
333
1

Ответы 1

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

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

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

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

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

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 меньше, чем количество ядер, которое у вас есть, чтобы убедиться, что принимающий код может иметь целое ядро).


2

1. Redhat has a nice doc on Network Performance Tuning. It's written more from the sysadmin's point of view than the programmer's, and it expects you to either know, or know how to look up, a lot of background information—but it should be helpful if you're willing to do that. You may also want to try searching Server Fault rather than Stack Overflow if you want to go down this road.

Другие вопросы по теме