How do I stop Tornado web server?

后端 未结 9 671
渐次进展
渐次进展 2020-12-08 06:48

I\'ve been playing around a bit with the Tornado web server and have come to a point where I want to stop the web server (for example during unit testing). The following sim

相关标签:
9条回答
  • 2020-12-08 07:24

    If you need this behavior for unit testing, take a look at tornado.testing.AsyncTestCase.

    By default, a new IOLoop is constructed for each test and is available as self.io_loop. This IOLoop should be used in the construction of HTTP clients/servers, etc. If the code being tested requires a global IOLoop, subclasses should override get_new_ioloop to return it.

    If you need to start and stop an IOLoop for some other purpose and can't call ioloop.stop() from a callback for some reason, a multi-threaded implementation is possible. To avoid race conditions, however, you need to synchronize access to the ioloop, which is actually impossible. Something like the following will result in deadlock:

    Thread 1:

    with lock:
        ioloop.start()
    

    Thread 2:

    with lock:
        ioloop.stop()
    

    because thread 1 will never release the lock (start() is blocking) and thread 2 will wait till the lock is released to stop the ioloop.

    The only way to do it is for thread 2 to call ioloop.add_callback(ioloop.stop), which will call stop() on thread 1 in the event loop's next iteration. Rest assured, ioloop.add_callback() is thread-safe.

    0 讨论(0)
  • 2020-12-08 07:29

    In case you do no want to bother with threads, you could catch a keyboard interrupt signal :

    try:
        tornado.ioloop.IOLoop.instance().start()
    # signal : CTRL + BREAK on windows or CTRL + C on linux
    except KeyboardInterrupt:
        tornado.ioloop.IOLoop.instance().stop()
    
    0 讨论(0)
  • 2020-12-08 07:31

    We want to use a multiprocessing.Process with a tornado.ioloop.IOLoop to work around the cPython GIL for performance and independency. To get access to the IOLoop we need to use Queue to pass the shutdown signal through.

    Here is a minimalistic example:

    class Server(BokehServer)
    
        def start(self, signal=None):
            logger.info('Starting server on http://localhost:%d'
                        % (self.port))
    
            if signal is not None:
                def shutdown():
                    if not signal.empty():
                        self.stop()
                tornado.ioloop.PeriodicCallback(shutdown, 1000).start()
    
            BokehServer.start(self)
            self.ioloop.start()
    
        def stop(self, *args, **kwargs):  # args important for signals
            logger.info('Stopping server...')
            BokehServer.stop(self)
            self.ioloop.stop()
    

    The Process

    import multiprocessing as mp
    import signal
    
    from server import Server  # noqa
    
    class ServerProcess(mp.Process):
        def __init__(self, *args, **kwargs):
            self.server = Server(*args, **kwargs)
            self.shutdown_signal = _mp.Queue(1)
            mp.Process.__init__(self)
    
            signal.signal(signal.SIGTERM, self.server.stop)
            signal.signal(signal.SIGINT, self.server.stop)
    
        def run(self):
            self.server.start(signal=self.shutdown_signal)
    
        def stop(self):
            self.shutdown_signal.put(True)
    
    if __name__ == '__main__':
        p = ServerProcess()
        p.start()
    

    Cheers!

    0 讨论(0)
  • 2020-12-08 07:32

    To stop the entire ioloop you simply invoke the ioloop.stop method when you have finished the unit test. (Remember that the only (documented) thread safe method is ioloop.add_callback, ie. if the unit tests is executed by another thread, you could wrap the stop invocation in a callback)

    If its enough to stop the http web server you invoke the httpserver.stop() method

    0 讨论(0)
  • 2020-12-08 07:34

    There is a problem with Zaar Hai's solution, namely that it leaves the socket open. The reason I was looking for a solution to stop Tornado is I'm running unit tests against my app server and I needed a way to start/stop the server between tests to have a clear state (empty session, etc.). By leaving the socket open, the second test always ran into an Address already in use error. So I came up with the following:

    import logging as log
    from time import sleep
    from threading import Thread
    
    import tornado
    from tornado.httpserver import HTTPServer
    
    
    server = None
    thread = None
    
    
    def start_app():
        def start():
            global server
            server = HTTPServer(create_app())
            server.listen(TEST_PORT, TEST_HOST)
            tornado.ioloop.IOLoop.instance().start()
        global thread
        thread = Thread(target=start)
        thread.start()
        # wait for the server to fully initialize
        sleep(0.5)
    
    
    def stop_app():
        server.stop()
        # silence StreamClosedError Tornado is throwing after it is stopped
        log.getLogger().setLevel(log.FATAL)
        ioloop = tornado.ioloop.IOLoop.instance()
        ioloop.add_callback(ioloop.stop)
        thread.join()
    

    So the main idea here is to keep a reference to the HTTPServer instance and call its stop() method. And create_app() just returns an Application instance configured with handlers. Now you can use these methods in your unit tests like this:

    class FoobarTest(unittest.TestCase):
    
        def setUp(self):
            start_app()
    
        def tearDown(self):
            stop_app()
    
        def test_foobar(self):
            # here the server is up and running, so you can make requests to it
            pass
    
    0 讨论(0)
  • 2020-12-08 07:37

    I just ran into this and found this issue myself, and using info from this thread came up with the following. I simply took my working stand alone Tornado code (copied from all the examples) and moved the actual starting code into a function. I then called the function as a threading thread. My case different as the threading call was done from my existing code where I just imported the startTornado and stopTornado routines.

    The suggestion above seemed to work great, so I figured I would supply the missing example code. I tested this code under Linux on a FC16 system (and fixed my initial type-o).

    import tornado.ioloop, tornado.web
    
    class Handler(tornado.web.RequestHandler):
        def get(self):
            self.write("Hello, world")
    
    application = tornado.web.Application([ (r"/", Handler) ])
    
    def startTornado():
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()
    
    def stopTornado():
        tornado.ioloop.IOLoop.instance().stop()
    
    if __name__ == "__main__":
        import time, threading
        threading.Thread(target=startTornado).start()
        print "Your web server will self destruct in 2 minutes"
        time.sleep(120)
        stopTornado()
    

    Hope this helps the next person.

    0 讨论(0)
提交回复
热议问题