DarkMatter in Cyberspace
  • Home
  • Categories
  • Tags
  • Archives

来自Python世界的一篇优秀的异步通信框架博文


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): : 5 ]

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模式与多线程模式各自的优缺点,但说的好像不是很清楚;



Published

Apr 17, 2012

Last Updated

Apr 17, 2012

Category

Tech

Tags

  • twisted 1

Contact

  • Powered by Pelican. Theme: Elegant by Talha Mansoor