如何停止Tornado网络服务器?

37 投票
9 回答
32709 浏览
提问于 2025-04-16 14:06

我最近在玩一个叫做Tornado web server的东西,到了想要停止这个网络服务器的时候,比如在做单元测试时。Tornado官网上有一个简单的例子可以参考

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

application = tornado.web.Application([
    (r"/", MainHandler),
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

当你调用tornado.ioloop.IOLoop.instance().start()时,它会让程序(或者当前的线程)停下来。查看源代码中的IOLoop对象,文档里有关于stop函数的例子:

To use asynchronous methods from otherwise-synchronous code (such as
unit tests), you can start and stop the event loop like this:
  ioloop = IOLoop()
  async_method(ioloop=ioloop, callback=ioloop.stop)
  ioloop.start()
ioloop.start() will return after async_method has run its callback,
whether that callback was invoked before or after ioloop.start.

不过,我不知道怎么把这个整合进我的程序里。我其实有一个类来封装这个网络服务器(它有自己的startstop函数),但一旦我调用start,程序(或者测试)就会被阻塞。

我尝试过在另一个进程中启动网络服务器(使用multiprocessing包)。这是封装网络服务器的类:

class Server:
    def __init__(self, port=8888):
        self.application = tornado.web.Application([ (r"/", Handler) ])

        def server_thread(application, port):
            http_server = tornado.httpserver.HTTPServer(application)
            http_server.listen(port)
            tornado.ioloop.IOLoop.instance().start()

        self.process = Process(target=server_thread,
                               args=(self.application, port,))

    def start(self):
        self.process.start()

    def stop(self):
        ioloop = tornado.ioloop.IOLoop.instance()
        ioloop.add_callback(ioloop.stop)

但是,stop似乎并没有完全停止网络服务器,因为在下一个测试中它仍然在运行,即使是这样的测试设置:

def setup_method(self, _function):
    self.server = Server()
    self.server.start()
    time.sleep(0.5)  # Wait for web server to start

def teardown_method(self, _function):
    self.kstore.stop()
    time.sleep(0.5)

我该如何在Python程序中启动和停止Tornado网络服务器呢?

9 个回答

12

如果你不想麻烦线程的问题,可以捕捉一个键盘中断信号:

try:
    tornado.ioloop.IOLoop.instance().start()
# signal : CTRL + BREAK on windows or CTRL + C on linux
except KeyboardInterrupt:
    tornado.ioloop.IOLoop.instance().stop()
20

这里有一个方法,可以从另一个线程停止Tornado。Schildmeijer给了一个很好的提示,但我花了一些时间才弄明白最终的有效示例。

请看下面的内容:

import threading
import tornado.ioloop
import tornado.web
import time


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world!\n")

def start_tornado(*args, **kwargs):
    application = tornado.web.Application([
        (r"/", MainHandler),
    ])
    application.listen(8888)
    print "Starting Torando"
    tornado.ioloop.IOLoop.instance().start()
    print "Tornado finished"

def stop_tornado():
    ioloop = tornado.ioloop.IOLoop.instance()
    ioloop.add_callback(ioloop.stop)
    print "Asked Tornado to exit"

def main():

    t = threading.Thread(target=start_tornado)  
    t.start()

    time.sleep(5)

    stop_tornado()
    t.join()

if __name__ == "__main__":
    main()
31

我刚遇到这个问题,自己也找到了这个问题的解决方法。根据这个讨论串的信息,我得出了以下结论。我简单地把我之前能正常运行的独立Tornado代码(从所有示例中复制的)中的启动代码放到了一个函数里。然后我把这个函数作为一个线程来调用。在我的情况下,线程的调用是从我现有的代码中进行的,我只是导入了startTornado和stopTornado这两个功能。

上面的建议看起来效果很好,所以我想提供缺失的示例代码。我在Linux的FC16系统上测试了这段代码(并修正了我最开始的一个打字错误)。

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()

希望这能帮助到下一个人。

撰写回答