首页 > 编程 > Python > 正文

Tornado 多进程实现分析详解

2020-02-16 11:36:36
字体:
来源:转载
供稿:网友

引子

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), 它的回调 会创建连接, 我注意到回调里的异常捕获有这样几行

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表