Commits

Sylvain Hellegouarch  committed 7e758fe

recipe to integrate the waitress server with CherryPy

  • Participants
  • Parent commits ca9a009

Comments (0)

Files changed (2)

File deployment/waitress_/__init__.py

+# -*- coding: utf-8 -*-
+__doc__ = """ Demonstrates on how to run
+a CherryPy application within a waitress WSGI server.
+
+It requires waitress http://docs.pylonsproject.org/projects/waitress/en/latest/
+
+This recipe provides two ways of integrating the waitress
+server with the CherryPy engine's loop. Either the engine is
+responsible for asking the waitress server to loop once or
+the waitress server is run into its own thread and, therefore,
+not dependant from the rate of the engine's loop.
+
+The second solution is probably less likely to cause unwanted
+delay for request processing since the server is able to
+run at its own pace. However this means running the
+server in a thread which may not be suitable for everyone.
+"""
+import cherrypy
+
+class Root(object):
+    @cherrypy.expose
+    def index(self):
+        return "Hello world waitress"
+
+if __name__=="__main__":
+    # Logging is performed by waitress
+    cherrypy.config.update({
+        'log.screen': True
+        }
+    ) 
+    
+    # Mount the application so that it
+    # served by the CherryPy application server
+    # The returned app is a WSGI application
+    app = cherrypy.tree.mount(Root())
+
+    # Siince we are using the waitress WSGI server
+    # we prevent the CherryPy HTTP server
+    # from being started
+    cherrypy.server.unsubscribe()
+
+    # Bus plugin that will run the waitress server
+    # whilst behaving properly with the bus itself
+    # Runs the server in the bus mainloop using the
+    # bus's loop internal rate.
+    # from waitressplugin import WaitressServerPlugin
+    # WaitressServerPlugin(app, host='0.0.0.0', port=8090).subscribe()   
+
+    # Bus plugin that will run the waitress server
+    # inside in its own thread independantly from
+    # the bus mainloop
+    from waitressplugin import ThreadedWaitressServerPlugin
+    ThreadedWaitressServerPlugin(app, host='0.0.0.0', port=8090).subscribe()
+    
+    # Finally we run the CherryPy application server
+    cherrypy.engine.start()
+    cherrypy.engine.block()

File deployment/waitress_/waitressplugin.py

+# -*- coding: utf-8 -*-
+import threading
+
+import cherrypy
+from cherrypy.process import plugins
+from waitress.server import WSGIServer as _WSGIServer
+
+__all__ = ['WaitressServerPlugin']
+
+class WSGIServer(_WSGIServer):
+    def loop(self, count=1, timeout=0.0):
+        self.asyncore.loop(timeout=timeout, map=self._map, use_poll=True, count=count)
+        
+    def shutdown(self):
+        self.task_dispatcher.shutdown()
+        
+class WaitressServerPlugin(plugins.SimplePlugin):
+    def __init__(self, wsgiapp, **kwargs):
+        """ Rather than using the asyncore loop to block
+        as waitress itself does, we use the CherryPy mainloop
+        to loop through the WSGIserver.
+
+        The advantage of this solution is that the server
+        runs in the main thread. The drawbacks is that the
+        server loops only when the bus tells it to which will
+        add some delay to the request processing.
+        """
+        plugins.SimplePlugin.__init__(self, cherrypy.engine)
+        self.server = WSGIServer(wsgiapp, **kwargs)
+
+    def start(self):
+        cherrypy.log("Starting the waitress server's loop")
+        cherrypy.log("Serving on http://%s:%s" % (self.server.effective_host,
+                                                  self.server.effective_port))
+        self.bus.subscribe('main', self.server.loop)
+            
+    def stop(self):
+        cherrypy.log("Stopping the waitress server's loop")
+        self.server.shutdown()
+        self.bus.unsubscribe('main', self.server.loop)
+
+class ThreadedWaitressServerPlugin(plugins.SimplePlugin):
+    def __init__(self, wsgiapp, **kwargs):
+        """ Run the waitress server loop into a dedicated thread
+        controlled by this plugin so that we can start and stop
+        the server along side the bus.
+
+        The advantage is that we let the server handle incoming
+        connections and data at its own rate, independantly from
+        the bus mainloop. The drawbacks is that the server then
+        runs in a different thread.
+        """
+        plugins.SimplePlugin.__init__(self, cherrypy.engine)
+        self.server = WSGIServer(wsgiapp, **kwargs)
+        self.runner = threading.Thread(target=self.run, name="waitress server")
+        self.runner.daemon = True
+
+    def start(self):
+        cherrypy.log("Starting the waitress server's loop")
+        cherrypy.log("Serving on http://%s:%s" % (self.server.effective_host,
+                                                  self.server.effective_port))
+        self.runner.start()
+            
+    def stop(self):
+        cherrypy.log("Stopping the waitress server's loop")
+        self.server.shutdown()
+        self.runner.join(timeout=1.0)
+
+    def run(self):
+        self.server.loop(count=None, timeout=30.0)