LINUX.ORG.RU

Python shared memory

 , ,


1

4

Добрый день.

У меня есть сервер для websocket, написанный на Tornado. В нем я определяю глобальный словарь в котором хранятся подключаемые юзеры.

...
# Создаю глобальный словарь
client_dict = {}

class MessagesHandler(tornado.websocket.WebSocketHandler):
        ...
    def open(self, v):
        ...
        # Запихиваю в словарь user_id: self, чтоб потом можно было его юзать для отдачи ответа 
        client_dict.update({self.user_id : self})
		...

    @tornado.gen.coroutine
    def on_message(self, mess):
        ...
        # Посылаю сообщение нужному юзеру, которого беру из словаря
        for k, recipient_wsconnection in client_dict[recipient_id].items():
            recipient_wsconnection.write_message('Ko ko ko')
    ...
...			

Все отлично работало пока не настало время это дело в продакшен нести.

При помощи supervisord я создаю несколько процессов этого сервера на разных портах. И при помощи nginx проксирую их во внешний мир.

И как можно догадаться адресное пространство разное и поэтому мой глобальный словарь работает только в том процессе в котором он был создан. (для меня было сложно, так как раньше я с этим дело не имел) То есть если один пользователь создан в одном процессе а другой в другом, то передавать сообщение между ними нельзя.

Вопрос: Как мне создать один глобальный массив для всех процессов?

При помощи supervisord я создаю несколько процессов этого сервера на разных портах.

Т.е. родственными отношениями они не связаны?

Как мне создать один глобальный массив для всех процессов?

Массив байтов - mmap. Но, мне кажется, тебе проще будет использовать БД.

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

Есть у меня редис! Пробовал, но у меня не получилось. self туда не запихивается, поэтому и делаю таки извращения.

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

Родственными отношениями не связаны..

На счет БД я не знаю как туда запихнуть инстанс MessagesHandler. pikcle?

Но нормальное ли это решение?

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

На счет БД я не знаю как туда запихнуть инстанс MessagesHandler. pikcle?

В виде строки.

Но нормальное ли это решение?

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

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

На офф сайте Tornado прочитал как деплоить в продакшн через nginx, там показано как через процессы. Так и сделал. Мне кажется это даже лучше решение чем треды.

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

Это сарказм или нет?

Это реальность.

pickle.loads и хранить это в Redis это норм решение или нет?

Нет. У Redis есть встроенная поддержка хэшей.

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

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

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

Не очень понял про хеши. Ну есть и что? Чем в данном случае хеши лучше чем просто хранить типа set и get ?

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

Не очень понял про хеши. Ну есть и что?

Используй хэш вместо pickle.loads

Чем в данном случае хеши лучше чем просто хранить типа set и get ?

Я бы спросил, что такое «просто хранить set и get», но не стану.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner
>>> import redis
>>> c = redis.StrictRedis()
>>> class Test:
...     def __init__(self):
...         self.h = 5
...
>>>
>>> g = Test()
>>> c.hset('user_hash', 'user_1', g)
1
>>> h = c.hget('user_hash', 'user_1')
>>> h
b'<__main__.Test object at 0x7f443c415b00>'

И?

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

Пока тебе хватит одного процесса. Когда он станет популярен(никогда) перепишешь на go/c++/whatever.

ванга.jpg

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

Это и есть «использование хэша». Правда, наследование от Redis... впрочем, в данном случае это неспособно ничего испортить.

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

А редисом не получится в общем.. Так как при десериализации получается копия объекта. А мне нужен именно тот объект.. Мне нужна именно шареная память.

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

при десериализации получается копия объекта. А мне нужен именно тот объект

Не используй сериализацию.

Мне нужна именно шареная память.

Redis и есть шареная память.

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

Я думаю ты не понимаешь о чем говоришь. Редис это просто in memory NoSQL. Он не может быть shared memory. У меня словарь с экземплярами класса. Сложного класса.

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

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

Я думаю ты не понимаешь о чем говоришь

Я думаю, что понимаю это гораздо лучше, чем ты. Гораздо.

Редис это просто in memory NoSQL

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

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

Ты спроектировал (взял из туториала по Tornado) архитектуру, из-за которой у тебя нет возможности использовать multiprocessing (хотя я не уверен даже насчет пригодности multiprocessing для твоих целей). Поэтому хранить данные в shared memory тебе пришлось бы вручную, а это упражнение явно слишком трудно для тебя. Самый реалистичный выход - перепроектировать структуры данных так, чтобы они ложились на Redis или на любую SQL СУБД.

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

У меня риал тайм мессенджер. Хотелось бы сделать все красиво и масштабируемо.

В таком случае нужно делать шарды инстансов сервера по хэшам от пользователя (см. например, что такое consistent hashing) и прибивать все запросы для конкретного пользоваться к определённому шарду где всё время в памяти поднято его состояние + нужно иметь очереди сообщений между шардами (если пользователь X отправляет сообщение пользователю Y, то сначала оно проходит через стейт пользователя X на инстансе U(i) где валидируется, далее по MQ пересылается на шард U(j) с состоянием пользователя Y где и ожидает получения). Это всё несколько геморно в реализации, потому без необходимости не стоит думать о масштабируемости.

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

Многопоточность (многопроцессорность) это не просто. Хранить данные это фигня, сложность в работе с ними. Это как с танзакциями в бд (там для этого применяют метод изолирования транзакций). Например у тя есть несколько процессов, которые работают с одним объектом в памяти одновременно. Я не знаю как с этим бороться python.

Спроектировать по другому это не получится, так как это архитектура Tornado. Когда создается websocket соединение, то создается объект класса MessagesHandler в моем случае. И дальше чтоб послать сообщение этому надо юзать объект этого класса. Как по другому то?

И еще раз: Я не нашел примера как можно было бы работать с объектом прямо в редисе. А если он выступает как shared memory, то работать надо именно в нем, а не создавать копии объектов. Если ты хочешь помочь, то расскажи как это делается и как работает (или ссылку дай на описание), а не кидайся словами.

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

Ух ё! Спасибо за совет! Реально геморно.. MQ у меня есть, ток для других целей (чтоб долгие запросы/вычисления делать «паралельно» в django).

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

Потому что так работает. Как еще то?

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