Twisted Introduction from KRONDO by Dave Peticolas.
Part 1: 非常好的解释了同步与异步处理方式的区别,以及适用的场合。
Part 2: 给出了基于socket的同步和异步客户端如何接收数据,随后引出了reactor模式,有利于理解twisted中的reactor对象。
Part 6:
twisted-client-3\get-poetry.py中回调的实现方法见“ 动态和静态类型语言中实现回调的方法 ”,另外twisted-client-3/get-poetry-1.py中的PoetryClientFactory.clientConnectionFailed方法定义在twisted.internet.protocol.ClientFactory(PoetryClientFactory的父类)中,所以它也是一个回调方法;
Part 7:
twisted-deferred/defer-1.py中:d.callback(arg)中的arg会作为参数传给got_poem,这个参数一般在got_poem方法中起“结果”的作用。got_poem可以带多个参数,但callback方法只能传一个,例如下面的代码可以运行通过:
def got_poem(res, r2 = 'default'):
print 'Your poem is served:'
print res + ': ' + r2
...
d.callback('This poem is so short.')
但下面的代码会报参数个数错误:
d.callback('This poem is so short.', 'default')
Part 10:
get-poetry中,生成deferred是在get_poetry方法中,fire deferred是在PoetryClientFactory的方法中,违反了第13节提到的对deferred“谁生成,谁fire”原则;
Part 11:
本节的Discussion中,对Figure 26的说明里提到了file descriptors,根据此处的说明,我猜想p ython网络编程中的file descriptors可以理解为一个socket;
Part 12:
"A Simple Client"一节里的client用到了netcat工具,不能在Windows平台上运行,也不是linux预安装的工具,需要手工下载安装,所以我们用XShell或者SecureCRT充当客户端,运行这个例子的方法是:首先启动Server:
python twisted-server-1/transformedpoetry.py --port 11000
然后客户端里运行: telnet localhost 11000 ,连上后(有"Connection established.")发送: 27:cummingsify.HERE IS MY POEM,
可以收到信息: 15:here is my poem,
上面发送的信息是 netstring 格式,"cummingsify.HERE IS MY POEM"是真正的报文,cummingsify是协议名称,"HERE IS MY POEM"是诗句,收到的信息也是netstring格式,可以看到返回诗句(here is my poem)就是发送诗句的cummingsify变换(变为小写)。
变换流程是:
main->
TransformFactory->
TransformProtocol.stringReceived(接收报文,解析出协议名称为 cummingsify)->
TransformFactory.transform(用getattr方法组装出xform_cummingsify方法名,然后调用xform_cummingsify方法处理poem)->
TransformFactory.xform_cummingsify->
TransformService.cummingsify(实际完成变换为小写的动作)->
返回TransformProtocol.stringReceived继续运行,发送处理后的报文(self.sendString);
关闭连接。
Part 13:
通过分析twisted-deferred/defer-10.py以及3个变体的输出的变化,观察嵌套deferred的行为:
变体1:倒数第二行"deferred_2.callback(3)"改为"deferred_2.callback(5)",对应部分的输出变为 Notice the output from the third callback is missing. callback_3 got 5
Note the argument to the inner deferred's callback()
说明inner deferred的输出将作为N+1 outer deferred的输入,本例中 deferred_2没有callback,所以输入5直接作为输出,送给callback_3作为输入;
变体2:倒数第二行"deferred_2.callback(3)"改为"deferred_2.errback(5)",对应部分的输出变为:
Notice the output from the third callback is missing.
Note the argument to the inner deferred's callback()
Unhandled error in Deferred:
Unhandled Error
Traceback (most recent call last):
Failure: builtin.int: 5
说明inner deferred失败的时候将运行N+1 outer deferred的errback,由于本例中N+1 outer deferred没有实现errback,所以异常作为Unhandled error在程序运行完后由系统输出,这一机制在Part 9对twisted-deferred/defer-unhandled.py的说明中进行了阐述;
变体3:在变体2的基础上,将"d.addCallback(callback_3)"变为"d.addCallbacks(callback_3, errback_3)",同时增加对errback_3的定义:
def errback_3(res):
print 'errback_3 got', res
return 33
对应部分的输出变为:
Notice the output from the third callback is missing.
errback_3 got [Failure instance: Traceback (failure with no frames):
Note the argument to the inner deferred's callback()
由于outer deferred链的第3对增加了errback(errback_3),所以errback_3以inner deferred失败返回的结果(一个Failure对象)作为输入开始运行。
twisted-client-6\get-poetry.py (简写为p6) 与 twisted-client-5\get-poetry.py(简写为p5)比较,p5从服务器下载诗句后自己进行转换,p6首先从服务器下载诗句,然后上传到服务器上进行转换,再下载下来;p6的3个参数分别是transform服务器的端口、下载诗句的服务器端口(可以有多个),例如"python twisted-client-6/get-poetry.py 10001 10002 10003"是首先从10002和10003服务器上下载诗句,然后上传到10001服务器上进行转换,再下载下来;
Part 14:
deferred.pause()使得pause之后的所有动作都推迟到unpause之后开始运行,所以pause之后addCallback(定义deferred)与callback(fire deferred)先后顺序是无所谓的。
service = ProxyService(*server_addr)中的星号是什么意思?
twisted-server-2/poetry-proxy.py代码分析:
这个proxy由3部分组成:server, service, client。server接收用户的连接请求,包括PoetryProxyFactory和PoetryProxyProtocol两个类;service是连接server和client的纽带,由ProxyService类实现;client从external server处下载诗句,由PoetryClientFactory和PoetryClientProtocol类实现。server通过调用service的get_poem方法从client中取诗句,然后送到用户端,做到这一点是由两套“异步”过程保证的:client端(下载诗句)过程和server端(下载完成后缓存并发送给用户)过程,两个过程的纽带是PoetryClientFactory.deferred(以下简称pcd)。
client过程是:当有数据发送过来时,缓存到poem中,当external server与自己断开时,认为诗句下载完毕,调用PoetryClientFactory.poem_finished(poem)方法fire pcd;
server过程是:当与用户建立连接(PoetryProxyProtocol.connectionMade)时,初始化一个service,调用service.get_poem()方法(在pcd中增加第一层callback set_poem:缓存诗句,并把诗句向下层callback传送,然后初始化client,开始client过程),然后向pcd增加第二层callback(向用户端传送诗句)和第三层callback(断开与客户端的连接)。
至此“诗句下载完毕后怎么办”就设置好了,只等下载完毕(client fire pcd),pcd的callback链开始运行。我们可以认为pcd是“当诗句下载完成时”。
我认为proxy2.0用defer.succeed实现“同步过程defer化”比1.0用maybeDeferred实现更优雅,至少maybeDeferred这个词不达意的名字(应该叫alwaysDeferred)就很容易误导开发者;
Part 15: tests/test_poetry.py的test_client方法中,被测试的是返回值d,d在PoetryClientFactory的构造函数中初始化(第43行),其callback链在test_client方法中设置(第87行),在PoetryClientFactory.poem_finished方法中fire(第48行),fire的参数是(已下载完毕的)poem,所以进入callback链后运行got_poem(poem),此方法将实际结果poem与预期结果TEST_POEM进行比较,验证二者是否一致。据此可知trial中测试的一般过程是(设生成测试结果(实际值)的对象为TDO,实际值(由TDO生成)为actual,预期值为exp,测试方法为TM):TDO负责生成d,在条件达到时以actual为参数fire d,TM在d的callback链上配置测试条件(如assertEquals(actual,exp)之类验证测试通过与否的方法),并且(TM)返回d,这样当运行测试时trial执行reactor.run()方法(所以测试代码里没有reactor.run()),一切开始运转。
高并发网络服务的思考 by Lucifer探讨了reactor模式与多线程模式各自的优缺点,但说的好像不是很清楚;