

二. twisted简单使用
twisted的本质是reactor,我们可以使用twisted的底层API(避开twisted便利的高层抽象)来使用reactor:
# 示例一 twisted底层API的使用 from twisted.internet import reacto from twisted.internet import main from twisted.internet.interfaces import IReadDescriptor import socket class MySocket(IReadDescriptor): def __init__(self, address): # 连接服务器 self.address = address self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect(address) self.sock.setblocking(0) # tell the Twisted reactor to monitor this socket for reading reactor.addReader(self) # 接口: 告诉reactor 监听的套接字描述符 def fileno(self): try: return self.sock.fileno() except socket.error: return -1 # 接口: 在连接断开时的回调 def connectionLost(self, reason): self.sock.close() reactor.removeReader(self) # 当应用程序需要终止时 调用: # reactor.stop() # 接口: 当套接字描述符有数据可读时 def doRead(self): bytes = '' # 尽可能多的读取数据 while True: try: bytesread = self.sock.recv(1024) if not bytesread: break else: bytes += bytesread except socket.error, e: if e.args[0] == errno.EWOULDBLOCK: break return main.CONNECTION_LOST if not bytes: return main.CONNECTION_DONE else: # 在这里解析协议并处理数据 print bytes
示例一可以很清晰的看到twisted的reactor本质:添加监听描述符,监听可读/可写事件,当事件来临时回调函数,回调完成之后继续监听事件。
需要注意:
套接字为非阻塞,如果为阻塞则失去了reactor的意义
我们通过继承IReadDescriptor来提供reactor所需要的接口
通过reactor.addReader将套接字类加入reactor的监听对象中
main.CONNECTION_LOST是twisted预定义的值,通过这些值它我们可以一定程度控制下一步回调(类似于模拟一个事件)
但是上面的MySocket类不够好,主要有以下缺点:
需要我们自己去读取数据,而不是框架帮我们读好,并处理异常
网络IO和数据处理混为一块,没有剥离开来
三. twisted抽象
twisted在reactor的基础上,建立了更高的抽象,对一个网络连接而言,twisted建立了如下三个概念:
Transports:网络连接层,仅负责网络连接和读/写字节数据
Protocols: 协议层,服务业务相关的网络协议,将字节流转换成应用所需数据
Protocol Factories:协议工厂,负责创建Protocols,每个网络连接都有一个Protocols对象(因为要保存协议解析状态)
twisted的这些概念和erlang中的ranch网络框架很像,ranch框架也抽象了Transports和Protocols概念,在有新的网络连接时,ranch自动创建Transports和Protocols,其中Protocols由用户在启动ranch时传入,是一个实现了ranch_protocol behaviour的模块,Protocols初始化时,会收到该连接对应的Transports,如此我们可以在Protocols中处理字节流数据,按照我们的协议解析并处理数据。同时可通过Transports来发送数据(ranch已经帮你读取了字节流数据了)。
和ranch类似,twisted也会在新连接到达时创建Protocols并且将Transport传入,twisted会帮我们读取字节流数据,我们只需在dataReceived(self, data)接口中处理字节流数据即可。此时的twisted在网络IO上可以算是真正的异步了,它帮我们处理了网络IO和可能遇到的异常,并且将网络IO和数据处理剥离开来,抽象为Transports和Protocols,提高了程序的清晰性和健壮性。
# 示例二 twisted抽象的使用
from twisted.internet import reactor
from twisted.internet.protocol import Protocol, ClientFactory
class MyProtocol(Protocol):
# 接口: Protocols初始化时调用,并传入Transports
# 另外 twisted会自动将Protocols的factory对象成员设为ProtocolsFactory实例的引用
# 如此就可以通过factory来与MyProtocolFactory交互
def makeConnection(self,trans):
print 'make connection: get transport: ', trans
print 'my factory is: ', self.factory
# 接口: 有数据到达
def dataReceived(self, data):
self.poem += data
msg = 'Task %d: got %d bytes of poetry from %s'
print msg % (self.task_num, len(data), self.transport.getPeer())
# 接口: 连接断开
def connectionLost(self, reason):
# 连接断开的处理
class MyProtocolFactory(ClientFactory):
# 接口: 通过protocol类成员指出需要创建的Protocols
protocol = PoetryProtocol # tell base class what proto to build
def __init__(self, address):
self.poetry_count = poetry_count
self.poems = {} # task num -> poem
# 接口: 在创建Protocols的回调
def buildProtocol(self, address):
proto = ClientFactory.buildProtocol(self, address)
# 在这里对proto做一些初始化....
return proto
# 接口: 连接Server失败时的回调
def clientConnectionFailed(self, connector, reason):
print 'Failed to connect to:', connector.getDestination()
def main(address):
factory = MyClientFactory(address)
host, port = address
# 连接服务端时传入ProtocolsFactory
reactor.connectTCP(host, port, factory)
reactor.run()示例二要比示例一要简单清晰很多,因为它无需处理网络IO,并且逻辑上更为清晰,实际上ClientFactory和Protocol提供了更多的接口用于实现更灵活强大的逻辑控制,具体的接口可参见twisted源代码。
四. twisted Deferred
twisted Deferred对象用于解决这样的问题:有时候我们需要在ProtocolsFactory中嵌入自己的回调,以便Protocols中发生某个事件(如所有Protocols都处理完成)时,回调我们指定的函数(如TaskFinished)。如果我们自己来实现回调,需要处理几个问题:
如何区分回调的正确返回和错误返回?(我们在使用异步调用时,要尤其注意错误返回的重要性)
如果我们的正确返回和错误返回都需要执行一个公共函数(如关闭连接)呢?
如果保证该回调只被调用一次?
Deferred对象便用于解决这种问题,它提供两个回调链,分别对应于正确返回和错误返回,在正确返回或错误返回时,它会依次调用对应链中的函数,并且保证回调的唯一性。
d = Deferred() # 添加正确回调和错误回调 d.addCallbacks(your_ok_callback, your_err_callback) # 添加公共回调函数 d.addBoth(your_common_callback) # 正确返回 将依次调用 your_ok_callback(Res) -> common_callback(Res) d.callback(Res) # 错误返回 将依次调用 your_err_callback(Err) -> common_callback(Err) d.errback(Err) # 注意,对同一个Defered对象,只能返回一次,尝试多次返回将会报错
twisted的defer是异步的一种变现方式,可以这么理解,他和thread的区别是,他是基于时间event的。
有了deferred,即可对任务的执行进行管理控制。防止程序的运行,由于等待某项任务的完成而陷入阻塞停滞,提高整体运行的效率。
Deferred能帮助你编写异步代码,但并不是为自动生成异步或无阻塞的代码!要想将一个同步函数编程异步函数,必须在函数中返回Deferred并正确注册回调。
五.综合示例
下面的例子,你们自己跑跑,我上面说的都是一些个零散的例子,大家对照下面完整的,走一遍。 twisted理解其实却是有点麻烦,大家只要知道他是基于事件的后,慢慢理解就行了。
#coding:utf-8
#xiaorui.cc
from twisted.internet import reactor, defer
from twisted.internet.threads import deferToThread
import os,sys
from twisted.python import threadable; threadable.init(1)
deferred =deferToThread.__get__
import time
def todoprint_(result):
print result
def running():
"Prints a few dots on stdout while the reactor is running."
# sys.stdout.write("."); sys.stdout.flush()
print '.'
reactor.callLater(.1, running)
@deferred
def sleep(sec):
"A blocking function magically converted in a non-blocking one."
print 'start sleep %s'%sec
time.sleep(sec)
print '
end sleep %s'%sec
return "ok"
def test(n,m):
print "fun test() is start"
m=m
vals = []
keys = []
for i in xrange(m):
vals.append(i)
keys.append('a%s'%i)
d = None
for i in xrange(n):
d = dict(zip(keys, vals))
print "fun test() is end"
return d
if __name__== "__main__":
#one
sleep(10).addBoth(todoprint_)
reactor.callLater(.1, running)
reactor.callLater(3, reactor.stop)
print "go go !!!"
reactor.run()
#two
aa=time.time()
de = defer.Deferred()
de.addCallback(test)
reactor.callInThread(de.callback,10000000,100 )
print time.time()-aa
print "我这里先做别的事情"
print de
print "go go end"更多剖析Python的Twisted框架的核心特性相关文章请关注PHP中文网!
