Search code examples
pythonservicetwistedshutdown

python/twisted, t.w.s.Site: safe shutdown


Having

class CnxAwareSite(server.Site):
    def __init__(self, *args, **kwargs):
        server.Site.__init__(self, *args, **kwargs)
        self.cnx_cnt = 0

    def buildProtocol(self, addr):
        channel = server.Site.buildProtocol(self, addr)

        def cntCnxMade(f):
            self.cnx_cnt += 1
            print 'new conn', self.cnx_cnt

            return f
        channel.connectionMade = cntCnxMade(channel.connectionMade)

        def cntCnxLost(f):
            def post_wrap(result):
                self.cnx_cnt -= 1
                return result

            return lambda *args, **kwargs: post_wrap(f(*args, **kwargs))
        channel.connectionLost = cntCnxLost(channel.connectionLost)

        return channel
...

site = CnxAwareSite(root)
site.port = reactor.listenTCP(PORT, site)

and having installed a trigger, performing async-looping checks for site.cnx_cnt deferred-chained to a site.port.stopListening()

reactor.addSystemEventTrigger('before', 'shutdown', shutdown, site)

is it the right way to ensure the 'clean shutdown' of the service before stopping the reactor?

Using twistd is not an option for me... (sigh)


Solution

  • In general, you've got basically the right idea. Your shutdown function (which is not shown, so I can't be sure about it) can return a Deferred that fires when any necessary cleanup is complete. However, I always make the relevant code into an IService, and then do reactor.addSystemEventTrigger('before', 'shutdown', myService.stopService); this makes it easier to test and it makes it possible to integrate your service into a twistd plugin service hierarchy later, if that possibility becomes available to you.

    Specifically, however, you don't need to do this just to stop listening on a port. The reactor will automatically handle that for you at shutdown time. Also: what do you mean by "performing async-looping checks"? If you're just waiting for the connection count to get to zero, then you should just fire a Deferred when it gets to zero, you shouldn't be looping.

    (More detail in your code sample would be useful to address some of these issues.)