Matthew Scragg
Matthew Scragg

Reputation: 4638

Python Gevent SocketIO memory leak disconnect not firing

I've been struggling with this for hours now. It's a pretty simple app, similar to a basic chat, except the server sends messages to the clients only. The basis of my problem is a memory leak. I tried re-factoring the app to be more stateless. I have followed this guide to tracking memory leaks. I couldn't make much sense of the graphs. I might go more in depth later. Firstly I noticed the disconnect was not firing and it does do some cleanup such as kill the greenlets. Why is the disconnect not firing? If anyone can give me some insight it's much appreciated.

https://gist.github.com/a53d1acc83b06b16d263

from gevent import monkey; monkey.patch_all()
import gevent
import redis
import json
import phpserialize

from socketio import socketio_manage
from socketio.server import SocketIOServer
from socketio.namespace import BaseNamespace
from socketio.mixins import BroadcastMixin, RoomsMixin

r = redis.StrictRedis(host='localhost', port=6379)

class ShoutsNamespace(BaseNamespace):

    def listener(self, room):
        r = redis.StrictRedis()
        r = r.pubsub()

        r.subscribe(room)

        for m in r.listen():
            if m['type'] == 'message':
                data = json.loads(m['data'])
                self.emit("shouts", data)

    def initialize(self):
        print 'Connected'

    def on_join(self, room):
        self.emit("shouts", json.loads(r.hget('ffxi.shouts', room)))
        self.spawn(self.listener, 'ffxi.shouts:'+room)
        return True

    def recv_disconnect(self):
        print 'Disconnected'
        self.disconnect(silent=True)

class PingNamespace(BaseNamespace):
    def on_ping(self, message):
        print message

class Application(object):
    def __init__(self):
        self.buffer = []

    def __call__(self, environ, start_response):
        path = environ['PATH_INFO'].strip('/')
        if path.startswith('static/'):
            try:
                data = open(path).read()
            except Exception:
                return not_found(start_response)

            if path.endswith(".js"):
                content_type = "text/javascript"
            elif path.endswith(".css"):
                content_type = "text/css"
            elif path.endswith(".swf"):
                content_type = "application/x-shockwave-flash"
            else:
                content_type = "text/html"

            start_response('200 OK', [('Content-Type', content_type)])
            return [data]

        if path.startswith("socket.io"):
            socketio_manage(environ, {'/ping': PingNamespace, '/shouts': ShoutsNamespace })
        else:
            return not_found(start_response)

def not_found(start_response):
    start_response('404 Not Found', [])
    return ['<h1>Not Found</h1>']


if __name__ == '__main__':
    SocketIOServer(('216.144.246.171', 443), Application(), resource="socket.io", policy_server=False, transports=['xhr-polling']).serve_forever()

Upvotes: 2

Views: 1874

Answers (1)

sv1jsb
sv1jsb

Reputation: 70

recv_disconnect is firing by the user, not the server. Either you have to make the user disconnect (adding a link or button?) or use events like "beforeunload" or "unload" to call it. In both situations you will need to call socket.disconnect().

Example:

$(window).on('beforeunload',function(){socket.disconnect();});

Hope this helped you a bit.

Best regards,

Andreas

Upvotes: 3

Related Questions