Python Twisted系列教程3:初步认识Twisted
作者:dave@http://krondo.com/our-eye-beams-begin-to-twist/ 译者:杨晓伟(采用意译)
可以从这里从头开始阅读这个系列。
用twisted的方式实现前面的内容
最终我们将使用twisted的方式来重新实现我们前面的异步模式客户端。不过,首先我们先稍微写点简单的twisted程序来认识一下twisted。
最最简单的twisted程序就是下面的代码,其在twisted-intro目录中的basic-twisted/simple.py中。
from twisted.internet import reactor
reactor.run()
可以用下面的命令来运行它:
python basic-twisted/simple.py
正如在第二部分所说的那样,twisted是实现了Reactor模式的,因此它必然会有一个对象来代表这个reactor或者说是事件循环,而这正是twisted的核心。上面代码的第一行引入了reactor,第二行开始启动事件循环。
这个程序什么事情也不做。除非你通过ctrl+c来终止它,否则它会一直运行下去。正常情况下,我们需要给出事件循环或者文件描述符来监视I/O(连接到某个服务器上,比如说我们那个诗歌服务器)。后面我们会来介绍这部分内容,现在这里的reactor被卡住了。值得注意的是,这里并不是一个在不停运行的简单循环。如果你在桌面上有个CPU性能查看器,可以发现这个循环体不会带来任何性能损失。实际上,这个reactor被卡住在第二部分图5的最顶端,等待永远不会到来的事件发生(更具体点说是一个调用select函数,却没有监视任何文件描述符)。
下面我们会让这个程序丰富起来,不过事先要说几个结论:
- Twisted的reactor只有通过调用reactor.run()来启动。
- reactor循环是在其开始的进程中运行,也就是运行在主进程中。
- 一旦启动,就会一直运行下去。reactor就会在程序的控制下(或者具体在一个启动它的线程的控制下)。
- reactor循环并不会消耗任何CPU的资源。
- 并不需要显式的创建reactor,只需要引入就OK了。
最后一条需要解释清楚。在Twisted中,reactor是Singleton(也是一种模式),即在一个程序中只能有一个reactor,并且只要你引入它就相应地创建一个。上面引入的方式这是twisted默认使用的方法,当然了,twisted还有其它可以引入reactor的方法。例如,可以使用twisted.internet.pollreactor
中的系统调用来poll
来代替
select
方法
。
若使用其它的
reactor
,需要在引入
twisted.internet.reactor
前安装它。下面是安装
pollreactor的方法
:
from twisted.internet import pollreactor pollreactor.install()
如果你没有安装其它特殊的
reactor
而引入了
twisted.internet.reactor
,那么
Twisted
会为你安装
selectreactor
。正因为如此,习惯性做法不要在最顶层的模块内引入
reactor
以避免安装默认
reactor
,而是在你要使用
reactor
的区域内安装。
下面
是使用
pollreactor
重写上上面的程序,可以在
basic-twisted/simple-poll.py文件中找到中找到:
from twited.internet import pollreactor pollreactor.install() from twisted.internet import reactor reactor.run()
上面这段代码同样没有做任何事情。
后面我们都会只使用默认的
reactor
,就单纯为了学习来说 ,所有的不同的
reactor
做的事情都一样。
你好,
Twisted
我们得用
Twisted
来做什么吧。下面这段代码在
reactor
循环开始后向终端打印一条消息:
def hello(): print 'Hello from the reactor loop!' print 'Lately I feel like I\'m stuck in a rut.' from twisted.internet import reactor reactor.callWhenRunning(hello) print 'Starting the reactor.' reactor.run()
这段代码可以在
basic-twisted/hello.py中找到。运行它,会得到如下结果:
Starting the reactor.
Hello from the reactor loop!
Lately I feel like I'm stuck in a rut.
仍然需要你手动来关掉程序,因为它在打印完毕后就又卡住了。
值得注意的是,
hello
函数是在
reactor
启动后被调用的。这意味是
reactor
调用的它,也就是说
Twisted
在调用我们的函数。我们通过调用
reactor
的
callWhenRunning
函数,并传给它一个我们想调用函数的引用来实现
hello
函数的调用。当然,我们必须在启动
reactor
之前完成这些工作。
我们使用回调来描述
hello
函数的引用。回调
实际上就是交给
Twisted
(或者其它框架)的一个函数引用,这样
Twisted
会在合适的时间调用这个函数引用指向的函数,具体到这个程序中,是在
reactor
启动的时候调用。由于
Twisted
循环是独立于我们的代码,我们的业务代码与
reactor
核心代码的绝大多数交互都是通过使用
Twisted
的
APIs
回调我们的业务函数来实现的。
我们可以通过下面这段代码来观察
Twisted
是如何调用我们代码的:
import traceback def stack(): print 'The python stack:' traceback.print_stack() from twisted.internet import reactor reactor.callWhenRunning(stack) reactor.run()
这段代码的文件是
basic-twisted/stack.py。不出意外,它的输出是:
The python stack:
... reactor.run() <-- This is where we called the reactor
... ... <-- A bunch of Twisted function calls ...
traceback.print_stack() <-- The second line in the stack function
不用考虑这其中的若干
Twisted
本身的函数。只需要关心
reactor.run()
与我们自己的函数调用之间的关系即可。
有关回调的一些其它说明:
Twisted
并不是唯一使用回调的框架。许多历史悠久的框架都已在使用它。诸多
GUI
的框架也是基于回调来实现的,如
GTK
和
QT
。
交互式程序的编程人员特别喜欢回调。也许喜欢到想嫁给它。也许已经这样做了。但下面这几点值得我们仔细考虑下:
reactor
模式是单线程的。
像
Twisted
这种交互式模型已经实现了
reactor
循环,意味无需我们亲自去实现它。
我们仍然需要框架来调用我们自己的代码来完成业务逻辑。
因为在单线程中运行,要想跑我们自己的代码,必须在
reactor
循环中调用它们。
reactor
事先并不知道调用我们代码的哪个函数
这样的话,回调并不仅仅是一个可选项,而是游戏规则的一部分。
图
6
说明了回调过程中发生的一切:
图
6 reactor
启用回调
图
6
揭示了回调中的几个重要特性:
- 我们的代码与Twisted代码运行在同一个进程中。
- 当我们的代码运行时,Twisted代码是处于暂停状态的。
- 同样,当Twisted代码处于运行状态时,我们的代码处于暂停状态。
- reactor事件循环会在我们的回调函数返回后恢复运行。
在一个回调函数执行过程中,实际上
Twisted
的循环
是
被有效地阻塞在我们的代码上
的
。因此,因此我们应该确保回调函数不要浪费时间(尽快返回)。特别需要强调
的是,我们应该尽量避免在回调函数中使用会阻塞
I/O
的函数。否则,我们将失去所有使用
reactor
所带来的优势。
Twisted
是不会采取特殊的预防措施来防止我们使用可阻塞的代码的,这需要我们自己来确保上面的情况不会发生。正如我们实际所看到的一样,对于普通网络
I/O
的例子,由于我们让
Twisted
替我们完成了异步通信,因此我们无需担心上面的事情发生。
其它也可能会产生阻塞的操作是读或写一个非
socket
文件描述符(如管道)或者是等待一个子进程完成。
如何从阻塞转换到非阻塞操作取决你具体的操作是什么,但是也有一些
Twisted APIs
会帮助你实现转换。值得注意的是,很多标准的
Python
方法没有办法转换为非阻塞方式。例如,
os.system
中的很多方法会在子进程完成前一直处于阻塞状态。这也就是它工作的方式。所以当你使用
Twisted
时,避
开
使用
os.system
。
退出
Twisted
原来我们可以使用
reactor
的
stop
方法来停止
Twisted
的
reactor
。但是一旦
reactor
停止就无法再启动了。(
Dave
的意思是,停止就退出程序了),因此只有在你想退出程序时才执行这个操作。
下面是退出代码,代码文件是
basic-twisted/countdown.py:
class Countdown(object): counter = 5 def count(self):
if self.counter == 0:
reactor.stop()
else:
print self.counter, '...'
self.counter -= 1
reactor.callLater(1, self.count) from twisted.internet import reactor reactor.callWhenRunning(Countdown().count) print 'Start!'
reactor.run()
print 'Stop!'
在这个程序中使用了
callLater
函数为
Twisted
注册了一个回调函数。
callLater
中的第二个参数是回调函数,第一个则是说明你希望在将来几秒钟时执行你的回调函数。那
Twisted
如何来在指定的时间执行我们安排好的的回调函数。由于程序并没有监听任何文件描述符,为什么它没有像前那些程序那样卡在
select
循环上?
select
函数,或者其它类似的函数,同样会接纳一个超时参数。如果在只提供一个超时参数值并且没有可供
I/O
操作的文件描述符而超时时间到时,
select
函数同样会返回。因此,如果设置一个
0
的超时参数,那么会无任何阻塞地立即检查所有的文件描述符集。
你可以将超时作为图
5
中循环等待中的一种事件来看待。并且
Twisted
使用超时事件来确保那些通过
callLater
函数注册的延时回调在指定的时间执行。或者更确切的说,在指定时间的前后会执行。如果一个回调函数执行时间过长,那么下面的延时回调函数可能会被相应的后延执行。
Twisted
的
callLater
机制并不为硬实时系统提供任何时间上的保证。
下面是上面程序的输出:
Start!
5 ...
4 ...
3 ...
2 ...
1 ...
Stop!
捕获
它,
Twisted
由于
Twisted
经常会在回调中结束调用我们的代码,因此你可能会想,如果我们的回调函数中出现异常会发生什么状况。(
Dave
的意思是说,在结束我们的回调函数后会再次回到
Twisted
代码中,若在我们的回调中发生异常,那是不是异常会跑到
Twisted
代码中,而造成不可想象的后果 )让我们来试试,在
basic-twisted/exception.py中的程序会在一个回调函数中引发一个异常,但是这不会影响下一个回调:
def falldown():
raise Exception('I fall down.') def upagain():
print 'But I get up again.'
reactor.stop() from twisted.internet import reactor reactor.callWhenRunning(falldown)
reactor.callWhenRunning(upagain) print 'Starting the reactor.'
reactor.run()
当你在命令行中运时,会有如下的输出:
Starting the reactor. Traceback (most recent call last):
... # I removed most of the traceback
exceptions.Exception: I fall down.
But I get up again.
注意
,尽管我们看到了因第一个回调函数引发异常而出现的跟踪栈,第二个回调函数依然能够执行。如果你将
reactor.stop()
注释掉的话,程序会继续运行下去。所以说,
reactor
并不会因为回调函数中出现失败(虽然它会报告异常)而停止运行。
网络服务器通常需要这种健壮的软件。它们通常不希望由于一个随机的
Bug
导致崩溃。
也并不是说当我们发现自己的程序内部有问题时,就垂头丧气。只是想说
Twisted
能够很好的从失败的回调中返回并继续执行。
请继续讲解诗歌服务器
现在,我们已经准备好利用
Twisted
来搭建我们的诗歌服务器。在第
4
部分,我们会实现我们的异步模式的诗歌服务器的
Twisted
版。
Python Twisted系列教程3:初步认识Twisted的更多相关文章
- Python Twisted系列教程4:由Twisted支持的诗歌客户端
作者:dave@http://krondo.com/twisted-poetry/ 译者:杨晓伟(采用意译) 你可以在这里从头开始阅读这个系列. 第一个twisted支持的诗歌服务器 尽管Twist ...
- Python Twisted系列教程5:由Twisted支持的诗歌客户端
作者:dave@http://krondo.com/twistier-poetry/ 译者:杨晓伟(采用意译) 你可以从这里从头开始阅读这个系列 抽象地构建客户端 在第四部分中,我们构建了第一个使用 ...
- python基础系列教程——Python中的编码问题,中文乱码问题
python基础系列教程——Python中的编码问题,中文乱码问题 如果不声明编码,则中文会报错,即使是注释也会报错. # -*- coding: UTF-8 -*- 或者 #coding=utf-8 ...
- python基础系列教程——Python3.x标准模块库目录
python基础系列教程——Python3.x标准模块库目录 文本 string:通用字符串操作 re:正则表达式操作 difflib:差异计算工具 textwrap:文本填充 unicodedata ...
- python基础系列教程——Python库的安装与卸载
python基础系列教程——Python库的安装与卸载 2.1 Python库的安装 window下python2.python3安装包的方法 2.1.1在线安装 安装好python.设置好环境变量后 ...
- python基础系列教程——Python的安装与测试:python的IDE工具PyDev和pycharm,anaconda
---恢复内容开始--- python基础系列教程——Python的安装与测试:python的IDE工具PyDev和pycharm,anaconda 从头开启python的开发环境搭建.安装比较简单, ...
- Python Twisted系列教程20: Twisted和Erlang
作者:dave@http://krondo.com/twisted-and-erlang/ 译者: Cheng Luo 你可以从”第一部分 Twist理论基础“开始阅读:也可以从”Twisted 入 ...
- Python Twisted系列教程7:小插曲,Deferred
作者:dave@http://krondo.com/an-interlude-deferred/ 译者:杨晓伟(采用意译) 你可以从这里从头开始阅读这个系列 回调函数的后序发展 在第六部分我们认识这 ...
- Python Twisted系列教程6:抽象地利用Twisted
作者:dave@http://krondo.com/and-then-we-took-it-higher/ 译者:杨晓伟(采用意译) 你可以从这里从头开始阅读这个系列. 打造可以复用的诗歌下载客户端 ...
- Python基础教程系列目录,最全的Python入门系列教程!
Python是一个高层次的结合了解释性.编译性.互动性和面向对象的脚本语言. 在现在的工作及开发当中,Python的使用越来越广泛,为了方便大家的学习,Linux大学 特推出了 <Python基 ...
随机推荐
- mqtt-jmeter
mqtt-jmeter https://github.com/emqtt/mqtt-jmeter mqtt-jmeter MQTT JMeter Plugin, it's used for testi ...
- springboot学习心得
1.mvn package --加载运行一个含有pom.xml的目录并生成target目录2.mvn dependency:tree 显示项目所有依赖的树状结构3.业务委托给了Spring Boot的 ...
- 启动代码和Bootloader区别
很多人都会把两者混淆,所以这里稍微说一下. 启动代码和Bootloader有点类似于类和结构体的关系,也就是说结构体只是一种很特殊很特殊的类,但不能说类是一种结构体. 也就是说可以说启动代码是Boot ...
- 什么是词性标注(POS tagging)
词性标注也叫词类标注,POS tagging是part-of-speech tagging的缩写. 维基百科对POS Tagging的定义: In corpus linguistics, part-o ...
- [蓝桥杯]ALGO-90.算法训练_出现次数最多的整数
问题描述 编写一个程序,读入一组整数,这组整数是按照从小到大的顺序排列的,它们的个数N也是由用户输入的,最多不会超过20.然后程序将对这个数组进行统计,把出现次数最多的那个数组元素值打印出来.如果有两 ...
- Wireshark抓包工具--TCP数据包seq ack等解读
1.Wireshark的数据包详情窗口,如果是用中括号[]括起来的,表示注释,在数据包中不占字节 2.在二进制窗口中,如“DD 3D”,表示两个字节,一个字节8位 3.TCP数据包中,seq表示这个包 ...
- 利用itext导出PDF的小例子
我这边使用的jar包: itext-2.1.7.jar itextasian-1.5.2.jar 代码,简单的小例子,导出pdf: PdfService.java: package com.cy.se ...
- PAT 乙级 1036 跟奥巴马一起编程(15) C++版
1036. 跟奥巴马一起编程(15) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue 美国总统奥巴马不仅呼吁所有人 ...
- WAL基础
WAL(Write-ahead logging,预写式日志)是数据库系统提供原子性和持久化的一系列技术. 在使用WAL的系统中,所有的修改都先被写入到日志中,然后再被应用到系统状态中.通常包含redo ...
- Jmeter(十)检查点
检查点又名断言,我们在手工测试过程中肉眼以及自己的逻辑思维对实际结果进行判断是否与预期结果一致,但是工具是死的,没有眼睛,没有思维,并不知道需要判断的信息在哪块,或者是来判断什么东西,我们需要让工具更 ...