Форум сайта python.su
Здравствуйте. Пишу сервер с использованием 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'
Отредактировано evil_rabbit (Авг. 10, 2012 07:28:15)
Офлайн
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__.
Но это мое предположение, нужно будет поинвестигейтить.
Офлайн
Ну вот у меня как раз был определён __del__()
Гонял тесты в 4 потока каждые 1,5 секунды создавал новое подключение (примерно через 40 секунд они отваливались по таймауту). Потребление памяти росло примерно на метр каждые 2 минуты.
Сделал del gc.garbage по рандому в on_open, вроде, держится стабильно. Но это пока не окончательный вариант сервера и от костыля хотелось бы избавиться.
В понедельник выпилю __del__() попробую без него.
Офлайн
Сделал без __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.subscriptions = {}
Отредактировано evil_rabbit (Авг. 26, 2012 05:57:11)
Офлайн