Tornado 多进程实现分析详解

引子

Tornado 是一个网络异步的的web开发框架, 并且可以利用多进程进行提高效率, 下面是创建一个多进程 tornado 程序的例子.

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
import time

import tornado.web
import tornado.httpserver
import tornado.ioloop
import tornado.netutil
import tornado.process

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

if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
          return
        raise

发现在创建连接的时候会跳过这个异常呢, 为什么?那么 EWOULDBLOCK 和 EAGAIN是是什么呢? 通过查找知道它的意思是在非阻塞模式下, 不需要重读或重写, EAGAIN 是 EWOULDBLOCK 在 Windows 上的名字, 所以看到这里就很明确了.

结论

Tornado 多进程的处理流程是先创建 socket, 然后再 fork 子进程, 这样所有的子进程实际都监听 一个(或多个)文件描述符, 也就是都在监听同样的 socket.

当连接过来所有的子进程都会收到可读事件, 这时候所有的子进程都会跳到 accept_handler 回调函数, 尝试建立连接.

一旦其中一个子进程成功的建立了连接, 当其他子进程再尝试建立这个连接的时候就会触发 EWOULDBLOCK (或 EAGAIN) 错误. 这时候回调函数判断是这个错误则返回函数不做处理.

当成功建立连接的子进程还在处理这个连接的时候又过来一个连接, 这时候就会有另外一个 子进程接手这个连接.

Tornado 就是通过这样一种机制, 利用多进程提升效率, 由于连接只能由一个子进程成功创建, 同一个请求也就不会被多个子进程处理.

后记

写完才发现, 我所使用的代码是 tornado-2.4.post2 版本, 当前最新代码是 3.3.0, 查看了下最新代码, 最新代码 TCPServer 写到单独 tornado.tcpserver 里了, 其他和本文 相关的并没有什么大的变化.

Category:PythonTagged:Pythonfork_processestornado多进程web提升效率

以上就是本文关于Tornado 多进程实现分析详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

您可能感兴趣的文章:

  • Tornado Web服务器多进程启动的2个方法
(0)

相关推荐

  • Tornado Web服务器多进程启动的2个方法

    一.Tornado简介 Tornado 是 FriendFeed 的 Web 服务器及其常用工具的开源版本.Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快.得利于其 非阻塞的方式和对epoll的运用,Tornado 每秒可以处理数以千计的连接,因此 Tornado 是实时 Web 服务的一个理想框架. 二.多进程启动方法 正常启动方法: 复制代码 代码如下: server = HTTPServer(app)

  • Tornado 多进程实现分析详解

    引子 Tornado 是一个网络异步的的web开发框架, 并且可以利用多进程进行提高效率, 下面是创建一个多进程 tornado 程序的例子. #!/usr/bin/env python # -*- coding:utf-8 -*- import os import time import tornado.web import tornado.httpserver import tornado.ioloop import tornado.netutil import tornado.proces

  • 分析详解python多线程与多进程区别

    目录 1 基础知识 1.1 线程 1.2 进程 1.3 两者的区别 2 Python 多进程 2.1 创建多进程 方法1:直接使用Process 方法2:继承Process来自定义进程类,重写run方法 2.2 多进程通信 Queue Pipe 2.3 进程池 3 Python 多线程 3.1 GIL 3.2 创建多线程 方法1:直接使用threading.Thread() 方法2:继承threading.Thread来自定义线程类,重写run方法 3.3 线程合并 3.4 线程同步与互斥锁 3

  • Java逃逸分析详解及代码示例

    概念引入 我们都知道,Java 创建的对象都是被分配到堆内存上,但是事实并不是这么绝对,通过对Java对象分配的过程分析,可以知道有两个地方会导致Java中创建出来的对象并一定分别在所认为的堆上.这两个点分别是Java中的逃逸分析和TLAB(Thread Local Allocation Buffer)线程私有的缓存区. 基本概念介绍 逃逸分析,是一种可以有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法.通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的

  • python 多进程队列数据处理详解

    我就废话不多说了,直接上代码吧! # -*- coding:utf8 -*- import paho.mqtt.client as mqtt from multiprocessing import Process, Queue import time, random, os import camera_person_num MQTTHOST = "172.19.4.4" MQTTPORT = 1883 mqttClient = mqtt.Client() q = Queue() # 连

  • Python日志打印里logging.getLogger源码分析详解

    实践环境 WIN 10 Python 3.6.5 函数说明 logging.getLogger(name=None) getLogger函数位于logging/__init__.py脚本 源码分析 _loggerClass = Logger # ...略 root = RootLogger(WARNING) Logger.root = root Logger.manager = Manager(Logger.root) # ...略 def getLogger(name=None): "&quo

  • C++ 匈牙利算法案例分析详解

    匈牙利算法是由匈牙利数学家Edmonds于1965年提出,因而得名.匈牙利算法是基于Hall定理中充分性证明的思想,它是部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法. -------等等,看得头大?那么请看下面的版本: 通过数代人的努力,你终于赶上了剩男剩女的大潮,假设你是一位光荣的新世纪媒人,在你的手上有N个剩男,M个剩女,每个人都可能对多名异性有好感(-_-||暂时不考虑特殊的性取向),如果一对男女互有好感,那么你就可以把这一对撮合在一起,现在

  • Java AbstractMethodError案例分析详解

    背景 AbstractMethodError异常对于我来说还是比较不常遇见的,最近有幸遇到,并侥幸的解决了,在这里把此种场景剖析一番,进入正题,下面是AbstractMethodError在Java的异常机制中所处的位置: 现在明确了AbstractMethodError所具有的特性: 1.它是Error的子类,Error类及其子类都是被划分在非检查异常之列的,就是说这些异常不能在编译阶段被检查出来,只能在运行时才会触发. 2.通过API文档里面的解释大致得出的结论就是说A依赖于B,但是执行的时

  • Android WindowManger的层级分析详解

    目录 一. Window 分类 二. Window层级 (1)应用程序窗口: (2)子窗口: (3)系统窗口: (三)如何真正查看 Window 的优先级 (四) 层级高低具体分析(对比Toast以及软键盘) (五)如何定制系统层级 一. Window 分类 应用 Window(ApplicationWindow: 对应一个 Acitivity) 子 Window    (SubWindow:不能单独存在,需要依附在特定的父 Window 中,比如常见的一些 Dialog 就是一个子 Windo

  • java面向对象设计原则之迪米特法则分析详解

    目录 概念 使用 拓展 概念 迪米特法则解决类与类之间耦合度问题,如果类A调用了B类的某一个方法,则这两个类就形成了一种紧耦合的方式,当B类这个方法发生变化时,一定会影响A类的执行结果.迪米特法则要求每一个类尽可能少的与其他类发生关系,也就是尽可能少的让其他类发生变化时,对其代码的执行结果产生的影响降到最低. 典型情况:A类调用B类的方法,B类和C类是一种关联关系,如果A类通过B类所持有的C类对象直接调用C类的方法,则A类和C类同时拥有强耦合的关系.代码如下: public class B {

  • java方法及this关键字原理分析详解

    目录 步骤1 .给顾客增加一个吃饭的方法 步骤 2 . 没有加static的属性和方法,一定需要先new对象 步骤 3 . 用new出来的对象去执行eat方法 步骤 4 . 怎么理解c.eat() 步骤 5 . 消息接受器 步骤 6 . 如果有两个顾客? 步骤 7 . 答案 步骤 8 .其实有个this 步骤 9 . 在eat方法里面直接使用this 步骤 10 . 构造方法 步骤 11 . 总结:this的意义是什么? 步骤 12 . 道理我都懂,那static又是什么? 步骤 13 . 本节

随机推荐