LINUX.ORG.RU

«Правильные» коннекты к Redis из python

 ,


0

5

Пытаюсь писать много данных в redis из python. Отправляю например 10к сообщений в секунду. Проблема в том что первые 2-3к залетают норм, а потом начинает сыпать такую ошибку

redis.exceptions.ConnectionError: Error 10048 connecting to 192.168.1.201:6379. Only one usage of each socket address (protocol/network address/port) is normally permitted.

При этом периодически все равно до 1к пролазит раз в несколько секунд.

делаю так

r = redis.StrictRedis(host=host, port=port, db=db)
r.lpush(redis_key, data)
r.close()

вроде бы закрываю соединение каждый раз. Но из-за этого ли. Читал про пул коннектов, но не очень понимаю как сделать так что бы он висел в памяти при запуске flask и при каждом новом запросе к апи не пересоздавался заново.


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

Так создайте один раз объект redis при инициализации (там же где делаете app = Flask(), например red = redis.StrictRedis(host=host, port=port, db=db)) и далее используйте везде red.lpush(redis_key, data), не закрывая соединение. Можно и в app.redis засунуть. Потом использовать app.redis.lpush()

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

ну вот собственно про это и вопрос, да, как правильно во фласке повесить «глобальную» переменную, которая будет одна и та же от запроса к запросу. Гуглю про это, и встречаю местами что это в рамках процесса, то есть если несколько процессов будет запущено, например в gunicorn повешу 4 или 8, то для каждого эта переменная будет своя. С другой стороны нашел такую инфу https://flask.palletsprojects.com/en/0.12.x/api/#flask.g, но оно теперь вроде как работает только в рамках реквеста, так что не подходит

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

С другой стороны нашел такую инфу https://flask.palletsprojects.com/en/0.12.x/api/#flask.g, но оно теперь вроде как работает только в рамках реквеста, так что не подходит

Читаем текст по вашей ссылке про flask.g: Starting with Flask 0.10 this is stored on the application context

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

Да, уже прочитал, не увидел версию. Сейчас смотрю в сторону шаринга обьекта через multiprocessing.managers код такой

from multiprocessing.managers import BaseManager
import redis
import config

# Redis connection pool init, to access same pool from any api request
redis_connection_pool = redis.ConnectionPool(
    host=config.REDIS_HOST,
    port=config.REDIS_PORT,
    db=config.REDIS_DATABASE
)

def get_redis_connection_pool():
    return redis_connection_pool

manager = BaseManager(address=('', 50000), authkey=b'pass')
manager.register('get_redis_connection_pool', get_redis_connection_pool)
server = manager.get_server()
server.serve_forever()

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

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

например в gunicorn повешу 4 или 8, то для каждого эта переменная будет своя.

Сделай отдельный сервис, отвечающий только за работу с редисом.

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

Точно ли тебе нужен пул? Фласковое приложение у тебя под wsgi в многопотоке работает? Если нет, то просто создавай один инстанс класса-обёртки соединения на инстанс приложения. Но даже если в многопотоке, чего не советую, то между воркерами пул соединений шарить никакого смысла нет. И я, честно говоря, всё равно не понял твоей задумки шарингом пула, даже если бы это могло работать так как ты хочешь, это можно бы работать только при условии, что ты сам, а не wsgi-сервер, все процессы запускаешь.

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

Пул это попытка решить проблему которую я описал в начале. Вообще задача писать очень много данных в редис. Схема такая, есть апишка, на фласке допустим, в нее прилетает много запросов в виде POST с JSON с какими то данными. Нужно все json’ы складывать в redis. Проблема в том что я чего то не понимаю, потому что как я описал выше, если я пытаюсь отправить 10к таких запросов в секунду, то 3к залетают нормально, а дальше ошибки. Наверняка что то делаю не так, потому как наверняка оно должно и больше 10к в секунду без проблем складывать.

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

Проблема в том что я чего то не понимаю, потому что как я описал выше, если я пытаюсь отправить 10к таких запросов в секунду, то 3к залетают нормально, а дальше ошибки.

Проблема в том, что вместо использования одного коннекта к Редису, ты создаешь коннект на каждую операцию и удаляешь его после каждой операции записи. Нужно использовать один коннект, создав его при старте твоего приложения. И использовать его везде как глобальную переменную.

sigurd ★★★★★
()

Редис он однопоточный если что. Шарить коннекты между процессами смысла особого нет - несколько доп коннектов значения особого не имеют. При такой нагрузке имеет смысл посмотреть в сторону openresty с redis

Jopich1
()
Ответ на: комментарий от WitcherGeralt

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

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

А проблемы с тем что у одному ресурсу будут пытаться пролезть по несколько процессов сразу? Не получится конфликта? Пока что получилось реализовать стабильную работу через редис пул конектов, вот его я определяю глобально и все его юзают, но там он заточен на это, на то что может много прийти и попросить.

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

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

Откуда один ресурс возьмется? У каждого процесса (копии вашего приложения) будет свой собственный.

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

Синхронщина плохо утилизирует проц, как её ни маштабируй. Был бы у тебя асинхрон, ты бы просто раскидал по одному инстансу на ядро, и проблем не знал.

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

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

Ну а если действие простое то и пользуй связку openresty redis + keepalive upstream

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

вроде бы закрываю соединение каждый раз

Зачем? Если блокирующих операций нет - можно закрыть один раз на выходе. Иначе делаешь пул и для блокирующих берешь из него отдельный коннект (только в скрипт их не суй, а то не коннект, а весь редис встанет)

upcFrost ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.