LINUX.ORG.RU

Python asyncio speed limit

 , ,


0

2

Привет, пишу tcp сервер (или клиент, неважно) на asyncio, как реализовать лимит скорости? Пока додумался только делать словарь типа «таймстамп»:«размер» , пополнять его на каждый data_received() или self.transport.send() и считать среднюю скорость за последние 10 секунд, ну и периодически чистить из словаря старые данные. Если скорость превышена, то начинаем бить буфер на чанки и спать асинхронно. Вроде работает но не покидает ощущение что это костыль.. В Гугле по словам «python asyncio network speed limit» ноль инфы. Ещё прикол что этот словарь получается глобальный, и как бы не было race condition при доступе к нему из разных экземпляров класса asyncio.Protocol.

Поделитесь best practices, спасибо.

★★★★★
Ответ на: комментарий от firkax

Всё верно

Ну ок..


сейчас ты шлёшь сразу много гигабайтов одним вызовом

Если слать одним куском все что прилетело, а потом слип, то график скорости получается рваный как пила. Если же бить на куски и перемежать ( вставлять между ними) слип, то график скорости получается ровный. Вот такое соображение.

Bers666 ★★★★★
() автор топика
Ответ на: комментарий от cobold

В async коде могут быть data races. Они имеют немного другой характер, чем в многопотоке. Но тем не менее для них есть всякие примитивы синхронизации: https://devdocs.io/python~3.12/library/asyncio-sync#asyncio.Lock

rupert ★★★★★
()
Ответ на: комментарий от Bers666

Если слать одним куском все что прилетело

Вопрос скорее такой: тебе «прилетает» сразу много гигабайт? Если да, то вполне возможно уже тут проблема в архитектуре.

firkax ★★★★★
()
Ответ на: комментарий от Bers666

Если же бить на куски и перемежать ( вставлять между ними) слип, то график скорости получается ровный. Вот такое соображение.

Ё, ты ноду открыл с чанками, паздравляю!

anonymous
()
Ответ на: комментарий от rtxtxtrx

Вот тебе пример

лимитилок Корутин в секунду или одновременно исполняющихся Корутин - полно. Но нет ни одной чтоб ограничивала поток байтов в секунду.

Bers666 ★★★★★
() автор топика
Ответ на: комментарий от firkax

и ты за время передачи сотни кб успеваешь скачки скорости заметить?

да, прикинь, 300КБ на полной скорости, далее сон, далее повторить. Скорость скачет. Неаккуратненько.

Bers666 ★★★★★
() автор топика
Ответ на: комментарий от x905

а чем штатные не подошли

из нормальных есть только V2ray\Xray (но даже в нем нет спидлимита) и Gost. Они написано на Go, и имеют пару косяков.

Итак, вот критерии нормального прокси:

- аутентификация по логину/паролю
- без аутентефикации для определенных IP клиентов
- черный/белый список для доменов/IP вебсайтов
- умеет Http и Socks5
- Socks5 умеет UDP
- умеет резолв имен через кастомный резолвер, с кэшем
- умеет адекватный лог
- умеет лимиты скорости, как глобальный, так и на 1 клиента
- умеет лимит коннектов, как глобальный, так и на 1 клиента
- умеет слать как напрямую, так и через релей
- без утечек DNS и UDP

Bers666 ★★★★★
() автор топика
Ответ на: комментарий от Bers666

Итак, вот критерии нормального прокси

Сначала я думал что на лоре завелись люди, которые знают чего хотят, но потом увидел

без утечек DNS и UDP

И понял, что ничего не изменилось. Школьники хотят набигать и грабить корованы.

no-dashi-v2 ★★★
()
Ответ на: комментарий от cobold

Race condition не зависит от многопоточности, например что бы выставить состояние, нужно два действия, но перед вторым действием программа прерывается на обработку async функции, или есть какой то планировщик заданий который начал обработку, и в коде другой процедуры ломается установка состояния, которая идет через одну переменную над которой все задания работали.

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

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

В питоне все не так работает.

async def fetch(session, url):
   response = await session.get(url)
   ...

...
asyncio.gather([fetch(session, url) for url urls])

Там примерно так должно работать:

while True:
  если мы ранее переключили контекст (state проверяем)
    если сокет готов для чтения
      читаем из него данные и меняем state на первоначальный
    иначе
      меняем контекст
      continue
  выполняем контекст пока не встретим await
  затем меняем state и переключаем контекст

Те ничего в принципе сломаться не может.

Там основной цикл есть для проверки состояни, скрытый за asyncio.run()

rtxtxtrx ★★
()
Последнее исправление: rtxtxtrx (всего исправлений: 2)
Ответ на: комментарий от MOPKOBKA

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

cobold ★★★★★
()
Ответ на: комментарий от cobold

Нельзя точно предсказать какая из async процедур точно запустится, или как поведет себя планировщик задач. Еще можно предположить, что если два потока имеют доступ к одной переменной, то ее можно защитить, и избежать ошибки.

Так что я не думаю что эти ситуации сильно разные. Вызвать между шагом 1 и шагом 2 можно функцию которая на первый взгляд не кажется опасной.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)

Вместо словаря таймстемп:размер можно просто хранить таймстемп и разрешённый на этот момент размер. На любой другой таймстемп легко вычислить разрешённый размер и отсылать/читать не больше этого куска, обновляя в (now(), 0). Если разрешённый размер меньше некоторого порога, то уходим с sleep.

neumond
()
Ответ на: комментарий от cobold

Если какое-то действие, которое должно быть атомарным (для решаемой задачи, речь не про атомики в процессоре), содержит внутри себя await, то это уже можно засчитать как race condition (к примеру уязвимости time of check/time of use). Но само собой, явный await позволяет легко такое обнаружить и исправить. Race condition слишком широкое понятие.

neumond
()