引子
Tornado 是一個網絡異步的的web開發框架, 并且可以利用多進程進行提高效率, 下面是創建一個多進程 tornado 程序的例子.
#!/usr/bin/env python# -*- coding:utf-8 -*-import osimport timeimport tornado.webimport tornado.httpserverimport tornado.ioloopimport tornado.netutilimport tornado.processclass LongHandler(tornado.web.RequestHandler): def get(self): self.write(str(os.getpid())) time.sleep(10)if __name__ == "__main__": app = tornado.web.Application(([r'/', LongHandler], )) sockets = tornado.netutil.bind_sockets(8090) tornado.process.fork_processes(2) server = tornado.httpserver.HTTPServer(app) server.add_sockets(sockets) tornado.ioloop.IOLoop.instance().start()
上面代碼使用 tornado.process.fork_processes 創建了2個子進程, 同時用時訪問這個 服務兩次, 分別會返回兩個相鄰的pid. 可以看到 tornado 確實使用了兩個進程來同時完成任務.
我一直很好奇 tornado 是如何將請求調度到子進程, 多個子進程又如何不同時處理一個請求呢?
探究
我們首先是調用 tornado.netutil.bind_sockets 來創建一個 socket(或一個 socket 列表),
接著我們調用 tornado.process.fork_processes 來 fork 子進程, 閱讀此函數的代碼會發現這個函數僅僅是創建子進程, 然后主進程負責等待子進程, 如果子進 程退出則會根據條件重啟子進程, 如果子進程全部退出并不符合重啟條件,則主進程退出.
調用這個函數之后, 子進程中函數會返回, 子進程則繼續執行調用這個函數之后的代碼.
我們在 fork 子進程后做了如下操作.
server = tornado.httpserver.HTTPServer(app) server.add_sockets(sockets) tornado.ioloop.IOLoop.instance().start()
我們先看看 tornado.httpserver.HTTPServer.add_sockets 發現 HTTPServer是繼承的 tornado.netutil.TCPServer , add_sockets 也是實現在 TCPServer 中
tornado.netutil.TCPServer.add_sockets
def add_sockets(self, sockets): if self.io_loop is None: self.io_loop = IOLoop.instance() for sock in sockets: self._sockets[sock.fileno()] = sock add_accept_handler(sock, self._handle_connection, io_loop=self.io_loop)
主要是映射了下 socket 和 socket 對應的文件描述符, 我們看看它調用的 add_accept_handler
def add_accept_handler(sock, callback, io_loop=None): if io_loop is None: io_loop = IOLoop.instance() def accept_handler(fd, events): while True: try: connection, address = sock.accept() except socket.error as e: if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN): return raise callback(connection, address) io_loop.add_handler(sock.fileno(), accept_handler, IOLoop.READ)
我們知道 I/O多路復用 在處理服務端 socket 時, 當有連接請求過來時, 會觸發 可讀的事件, 此函數將 socket 在主事件循環中注冊讀事件(IOLoop.READ), 它的回調 會創建連接, 我注意到回調里的異常捕獲有這樣幾行
新聞熱點
疑難解答