Найти - Пользователи
Полная версия: Tornadio2. Memory leak. Лишние ссылки на объект.
Начало » Web » Tornadio2. Memory leak. Лишние ссылки на объект.
1
evil_rabbit
Здравствуйте. Пишу сервер с использованием Tornado+Tornadio2.
Когда подключается юзер через веб-сокеты, то управление передаётся обработчику UserHandler, вызывается метод on_open и т.д.
При закрытии соединения вызывается on_close().
В общем всё, как в хелло-ворлдах.
через sys.getrefcount я выяснил, что на обработчик есть 3+ ссылок.
class UserHandler(SocketConnection):
  def __del__(self):
    print 'user instance deleted'
  def on_open(self, request):
    print 'user connected'
    # то, что дальше запросто выпиливается, но количество ссылок на объект не уменьшается
  def on_close(self):
    print 'user disconnected'
Вопрос: как убить объект обработчика после завершения on_close(), чтобы он мне всё-таки написал “user instance deleted”?
Sleepwalker
SocketConnection ссылается на обьект Session посредством self.session.
В свою очередь Session ссылается на SocketConnection посредством self.conn.
Выходят такие себе цыклические ссылки. Но это не значит что они не обрабатываются сборщиком мусора:
Вот вырезка из документации по __del__

Circular references which are garbage are detected when the option cycle detector is enabled (it's on by default), but can only be cleaned up if there are no Python-level __del__() methods involved.

Т.е. хоть там и есть цыклические ссылки, они должны нормально утилизироватся, при условии что вы не будете там определять метод __del__.

Но это мое предположение, нужно будет поинвестигейтить.
evil_rabbit
Ну вот у меня как раз был определён __del__()
Гонял тесты в 4 потока каждые 1,5 секунды создавал новое подключение (примерно через 40 секунд они отваливались по таймауту). Потребление памяти росло примерно на метр каждые 2 минуты.
Сделал del gc.garbage по рандому в on_open, вроде, держится стабильно. Но это пока не окончательный вариант сервера и от костыля хотелось бы избавиться.
В понедельник выпилю __del__() попробую без него.
evil_rabbit
Сделал без __del__(). Всё равно течет, но это уже не важно.
Сейчас больше интересует другое. У меня в UserHandler есть метод subscribe(), он создаёт подключение к Redis и начинает его слушать
    @tornado.gen.engine
    def subscribe(self, channel, listener=None):
        if (channel in self.subscriptions and self.subscriptions[channel] != None) \
        or len(self.subscriptions)>=self.subscriptions_limit:
            print self.subscriptions
            raise SubscribeException
        redis = redis_pool.get()
        yield tornado.gen.Task(
            redis.subscribe,
            channel
        )
        if listener == None:
            redis.listen(self.common_listener)
        else:
            redis.listen(listener)
        self.subscriptions[channel] = redis
Т.е. клиент подписывается на какой-то канал (уведомления, чат и т.п.). Всё бы ничего, но внезапно, когда зашел со второго браузера, я обнаружил, что self.subscription используется всеми подключенными клиентами сразу, т.е. один подписался, другие уже не могут (дамп self.subscriptions перед порождением исключения). Почему такое происходит? Разве на каждое подключение не создаётся отдельный объект UserHandler со своими переменными?

UPD
Разобрался. Написал в on_open()
self.subscriptions = {}
А из самого класса убрал. Пойду учить матчасть.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB