Tornado 多进程实现分析

Fri 11 April 2014

引子

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

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

结论

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

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

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

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

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

后记

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

Category: Python Tagged: Python fork_processes tornado 多进程 web 提升 效率

comments


用bottle+mongodb写的blog程序支持mysql啦

Tue 14 August 2012

前面博文提到过,本人用bottle+mongodb实现了以blog程序,

最近有些空闲时间,就重新用mvc的模式重写了一下,因为mongodb太过耗费内存,对一些小型的vps太过吃力所以加入了mysql的支持,

虽然较上次有些完善,但是还是有很多不足代码放在了googlecode上,由于最近一直在使用git,索性也就新建了一个git的项目,之前svn的项目也会更新.

所以大家想浏览代码可以到下面两个地方去,有什么不足和建议还请指教,如果你也想加入进来,可以留言或发email给我:

git:http://code.google.com/p/linuxzen/source/browse/water

svn:http://code.google.com/p/sharepythoncode/source/browse/water/

Category: Python Tagged: 支持mysql web python MySQL mongodb bottle blog

comments

Read More

用bottle+mongodb写的一个blog程序

Sat 16 June 2012

我个人觉得更好更快的学习和掌握某个东西最好的方法就是使用它,多使用它.然后在一次次的解决问题中来快速掌握和了解它.你觉得呢?前段时间接触了bottle这个轻量web框架,和nosql数据库mongodb,为了掌握和了解这她们,我自己做了一个blog程序,参照了vimer.cn里的设计的物理设计.是用bottle作为web开发框架,mongodb作为后台数据库.主要实现功能:

前台显示文章: 按分类显示 按标签显示 按月份归档显示 最新文章 评论 后台管理: 管理文章 管理分类 管理评论 发表文章 用户验证

目录结构

water

+-app 程序目录

|----admin.py 后台管理

|----blog.py 前台显示

|----dbconn.py 数据库连接

|----encrypt.py包含加密函数

+-static 静态文件目录:包括js css image

+-views 模版目录

+-index.py 用于启动整个程序 …

Category: Python Tagged: 项目 实例 web python mongodb bottle

comments

Read More

bottle的cookie操作小记(获取不是在当前页面创建的cookie)

Sat 02 June 2012

这两天为用bottle+mongodb写的一个项目加上登录功能,无奈怎么都获取不到保存的cookie,文档给出让我们这样操作cookie的代码片段:

@route(/login)
def login ():
      username = request .forms .get(username )
      password = request .forms .get(password )
      if check_user_credentials(username, password):
           response .set_cookie("account", username, secret= some-secret-key)
           return "Welcome %s!You are now logged in." % username
      else :
           return "Login failed." 

@route(/restricted)
def restricted_area ():
      username = request .get_cookie …

Category: Python Tagged: web python cookie bottle

comments

Read More

使用beaker让bottle支持session

Sat 26 May 2012

bottle是一个小型web框架,很小只有一个文件,但功能确很强大,学起来也简单,简单和小巧的同时也有很多不足,某些功能支持还不是很完善,比如session.但是也有它自身的好处,我们可以自己或使用别的模块来扩展它,不像django,很强大,但是想要进一步扩展的时候确无从下手.我们可以把非常简单而强大的bottle自己动手将它变得更加强大和完善.

bottle小巧支持cookie但是不支持session.为了安全起见我们有时候希望使用的session.我们可以使用中间件beaker来扩展bottle,使我们的bottle应用支持session.废话不多说.首先beaker不是内置模块,我们首先来安装它.当然你可以网上下包手动安装,我们使用最简单的:

easy_install beaker

没有easy_install这个命令?google吧,装了之后还是没有,如过时win的话检查环境变量,将Python安装目录下的Scripts目录添加到环境变量.

安装好后我们如何使用它,下面一段带面是使用的:

#!/usr/bin/env python
from bottle import route, default_app, run, request
from beaker.middleware import SessionMiddleware

session_opts = {
                'session.type …

Category: Python Tagged: web session python bottle beaker

comments

Read More

Gentoo下搭建python web环境(nginx+bottle+virtualenv+uwsgi)

Wed 16 May 2012

最近根据Gentoo官方文档整了一台Gentoo的虚拟机,感觉还是不错的,决定放弃CentOS投奔Gentoo,这几天研究NoSQL mongodb和python的bottle框架,web.py效率不是很好,而且是类级,bottle使用装饰器(虽然对她还是懵懂阶段,但是貌似很强大).感觉bottle更加强大和接近python,没有封装太多东西,django只能依照她的思想来做自己的事,最后还是选择了bottle来进入python的web世界,web服务器同样选择nginx.virtualenv可以让一个应用有一个相对独立的环境,特别用于多解释器环境或者经常变更的环境.uwsgi是web和python的中间件(可以这么解释吧).

环境:

系统:Gentoo ip:192.168.3.1

好吧废话不多说,Gentoo安装过程这里不再详述,官方文档很详细,下面记录安装配置过程. 首先Gentoo没有默认安装vim,先安装vim:

emerge vim

安装配置Python

最新版的Gentoo安装完毕后默认使用python3.2,而我惯用Python2.7.先首先安装python2.7. Gentoo使用emerge包管理,安装Python2.7:

cd /usr/portage/dev-lang …

Category: Linux Tagged: web virtualenv uwsgi python Gentoo bottle nginx

comments

Read More

CentOS下搭建python web生产环境(nginx+web.py+uwsgi)

Thu 19 April 2012

前面都一篇文章介绍介绍了Ubuntu下web.py的开发环境搭建,这篇文章主要来介绍如何让web.py结合nginx来实现生产环境

首先使用环境介绍: 系统: CentOS 5.5 32位

Python版本:2.7.2

nginx:1.0.13

ip:192.168.3.3/24

由于CentOS默认自带都python(2.4.3)版本较低,所以我们采用手动编译安装python的方式来使用python 2.7.2

首先安装python 2.7.2

cd /usr/src/
mkdir python
cd python
#下载python2.7.2
wget http://www …

Category: Linux Tagged: web.py web uwsgi spawn-fcgi python nginx

comments

Read More
Page 1 of 1