基于 Python 生成器的 Tornado 协程异步

Fri 19 December 2014

Tornado 4.0 已经发布了很长一段时间了, 新版本广泛的应用了协程(Future)特性. 我们目前已经将 Tornado 升级到最新版本, 而且也大量的使用协程特性.

很长时间没有更新博客, 今天就简单介绍下 Tornado 协程实现原理, Tornado 的协程是基于 Python 的生成器实现的, 所以首先来回顾下生成器.

生成器

Python 的生成器可以保存执行状态 并在下次调用的时候恢复, 通过在函数体内使用 yield 关键字 来创建一个生成器, 通过内置函数 next 或生成器的 next 方法来恢复生成器的状态.

def test():
    yield 1

我们调用 test 函数, 此时并不会返回结果, 而是会返回一个生成器

>>> test()
<generator object test at 0x100b3b320>

我们调用其 next 方法则返回 yield 关键字之后的内容.

>>> t = test()
>>> t.next()
1

如果我们接着调用 next 方法, 后面又没有 yield 关键字继续返回的话, 会抛出一个 StopIteration 异常.

yield 关键字不仅仅能从生成器内部返回状态, 同时也可以将外部信息传递到生成器内部, 通过将 yeild 关键里赋值给变量, 并调用生成器的 send 方法来将对象传递到生成器 内部. 需要注意的是生成器的开始必须调用其 next 方法, 后面 send 方法调用的同时 也会触发 next 动作. 如果没有变量接收 yield 关键字那么 send 传递的值将会 被丢弃.

>>> def test():
    a = yield
    print(a)

首先调用 next 上面函数返回的生成器将返回 None, 如果这时候直接调用 next 将 会给生成器发送 None, 如果调用 send 发送一个值, 将打印这个值并抛出 StopIteration 异常.

一个简单地协程

以上就是实现协程的所有基础, 为了加深理解, 我们这里写一个小例子, 例子我们只使用协程 开启两个甚至多个死循环, 下面就是一个极其简单地例子::

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

from __future__ import absolute_import, print_function, division, with_statement


def loop1():
    """ 循环1负责抛出一个函数和对应的参数, 并接收结果
    """
    a = 0
    ret = 1
    while True:
        ret = yield sum, [a, ret]
        a, ret = ret, a
        print("Loop1 ret", ret)


def loop2():
    """ 循环2 负责接收函数并计算结果, 然后 yield 出结果
    """
    while True:
        func, args = yield
        yield func(args)
        print("Loop2")


l1 = loop1()
l2 = loop2()
tmp = l1.next()

for i in range(10):
    l2.next()
    ret = l2.send(tmp)
    tmp = l1.send(ret)

上面例子里 loop1 负责产生任务, loop2 负责执行任务, 主循环负责调度任务并将任务结果发回给 任务产生者.

Tornado 如何做的

我们首先看一个使用 Tornado 协程异步的例子

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

from __future__ import absolute_import, print_function, division, with_statement

from tornado import gen
from tornado import web
from tornado import httpclient


class ActionHandler(web.RequestHandler):

    @gen.coroutine
    def get(self):
        response = yield httpclient.AsyncHTTPClient().fetch("http://www.linuxzen.com")

        # ...

其实原理在上面简单地例子里已经讲清楚了, 我们来简单分析一遍上面的例子, 首先 Tornado 得到 ActionHandler.get 方法抛出(next)的一个任务, 然后异步的去执行任务, 当任务(网络请求)结束或 异常时 Tornado 取得事件通知然后将结果放回(send)到该方法中让该方法继续执行.

由于是异步的, 调用这个方法并不会阻塞其他任务执行.

这时候我们的方法其实就是上个例子 loop1 函数, 而 Tornado 调度并执行了其抛出的任务.

总结

Tornado 的协程异步可以让异步看起来是顺序执行的, 可以从一大串的 callback 中解脱出来.

Tornado 的协程异步并不是这三言两语能说清楚的, 其中有很复杂的封装和传递, 有兴趣可以自己 阅读源码.

Category: Python Tagged: Python generator coroutine 协程 生成器 Tornado

comments


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__ …

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

comments

Read More

发布一个基于Tornado的高效异步的HTTP客户端库

Thu 01 August 2013

前面的博文提到过使用tornado进行网络异步编程, 也使用tornado实现了一个高效的WebQQ机器人, 由于tornado内置的AsyncHTTPClient功能过于单一, 所以自己写了一个基于Tornado的HTTP客户端库, 鉴于自己多处使用了这个库, 所以从项目中提取出来, 写成一个单独库 tornadohttpclient

TornadoHTTPClient 是一个基于Tornado的高效的异步HTTP客户端库, 支持Cookie和代理, 目前仅在Python2.7平台上测试过, 不支持Python3

听取了仙子君的意见, 直接对tornado.curl_httpclient.CurlAsyncHTTPClient进行封装

安装

首先从git clone 下代码

git clone https://github.com/coldnight/tornadohttpclient.git

然后安装它

cd tornadohttpclient
python setup.py install

教程

GET

TornadoHTTPClient的get方法可以发起一个get请求

from …

Category: Python Tagged: tornado http client module Python 高效

comments

Read More

clubot更新: 使用SQLAlchemy重写数据库部分和改用Tornado MainLoop

Fri 26 April 2013

clubot在我的vps上跑了有一段时间了, 最近接触了SQLAlchemy 然后反观clubot的数据库代码部分, 感觉代码又遭有乱实在看不过眼, 所以就使用SQLAlchemy重写了数据库模块, 并将epoll的MainLoop改成仙子君所写的TornadoMainLoop

更新内容

  1. 数据库使用SQLAlchemy重写
  2. MainLoop改用TornadoMainLoop
  3. 改变代码结构, 清理部分代码
  4. history命令改为old, 并支持时间查询
  5. 废弃一些不常用的命令
  6. 改变数据库表结构
  7. 废弃channel功能, cd命令仅支持切换聊天和安静模式
  8. 删除一些不用的配置

如何升级

数据库表结构做了更改, 所以为了兼容之前的数据库本次表名前加上clubot_前缀, 并配以update.py脚本用以支持将旧的数据导入.

新的依赖

本次更新添加了依赖 …

Category: Python Tagged: clubot pyxmpp2 gtalk xmpp 更新 重写 SQLAlchemy tornado

comments

Read More

借用Tornado实现高效的WebQQ机器人

Tue 23 April 2013

之前有写过一篇文章介绍使用Pyxmpp2桥接QQ和xmpp的文章(这里).后来我打算将WebQQ单独出来运行, 一开始直接拷贝了pyxmpp2的mainloop, 但是跑起来问题多多, 所以我又研究了利用Tornado进行网络编程(这里), 所以我放弃了Pyxmpp2的mainloop,使用Tornado进行重写

首先放出项目代码

引子

WebQQ协议是一套基于HTTP的QQ协议, 而用Pythonurllib2库进行请求太慢, 因为HTTP本身就使用socket请求, 所以改用多路复用I/O模型, 而Tornado简单高效, 看过代码后可以轻松上手.平台兼容性很好, 所以选择Tornado作为网络框架.

原理

首先实现了一个 HTTPStream类, 其主要接口是add_request方法, 它接受一个必选参数:request …

Category: Python Tagged: tornado 高效 webqq 并发 协议

comments

Read More

使用Tornado进行网络异步编程

Mon 15 April 2013

Tornado

Tornado 是一款非阻塞可扩展的使用Python编写的web服务器和Python Web框架, 可以使用Tornado编写Web程序并不依赖任何web服务器直接提供高效的web服务.所以Tornado不仅仅是一个web框架而且还是一款可以用于生产环境的高效的web服务器

Torando 在Linux和FreeBSD上使用高效的异步I/O模型 epollkqueue来实现高效的web服务器, 所以 tornado在Linux上和FreeBSD系列性能可以达到最高

接口

当然我们可以不仅仅把Tornado看作是一个web框架和web服务器, 我们可以利用Tornado提供的接口进行高效的网络异步编程,

tornado.ioloop.IOLoop 提供了三个接口可以用于网络编程:

add_handler

def add_handler(self, fd, handler, events):
    self._handlers[fd] = stack_context.wrap(handler)
    self._impl.register(fd, events | self.ERROR …

Category: Python Tagged: Python tornado 网络 异步 编程

comments

Read More

vLog 一个使用Python编写的轻量级博客系统

Tue 05 February 2013

介绍

何为vLog

大家有人可能注意到博客改变了,是的前面也有文章提到从wordpress迁移到vlog,但是何为vLog这里给大家简要的说明一下,vLog是我使用Pythontornado框架和Jinja2模板引擎,基于MySQL数据的一个轻量级的博客系统,此系统功能比较薄弱,处于开发初期,使用Markdown的格式来抒写博文.

为什么vLog

vLog后台十分简单(可以说是简陋),功能也简单,就是一款简单的博客系统,提供了Python终端脚本,可以在终端来抒写博文, vLog使用一套非常简单的缓存系统,缓存使用memcached使得页面加载速度非常快.

为什么不vLog

相对与wordpress vlog非常简陋,仅仅提供简单的博客功能,而且使用Python编写主机方面支援不太多,虽然有SAEGAE的支援,但是我没弄过,所以没有支援SAEGAE(如果你有兴趣,可以添加相关支持)

安装

平台

  • Linux
  • python2.7
  • MySQL …

Category: Python Tagged: vLog tornado blog 博客 轻量 markdown

comments

Read More

部署Tornado时iptables引发的的一个问题

Sat 29 September 2012

今天在CentOS上部署了一个Tornado,使用nginx做代理,

tornado使用8888,端口,使用nginx作为反向代理,配置文件如下:

server {
    listen 80;
    server_name www.linuxzen.com;

    location / {
        proxy_pass_header Server;
        proxy_redirect off;
        proxy_set_header X-Real_IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_pass http://127.0.0.1:8888;
    }
}

iptables filter表的INPUT链是DROP的,所以添加如下规则:

iptables -A INPUT -p tcp  -s 127.0.0.1 --dport 8888 -j ACCEPT

但是访问nginx总是返回502 Bad …

Category: Linux Tagged: tornado nginx iptables bad gateway 502

comments

Read More
Page 1 of 1