作者:dave@http://krondo.com/deferred-all-the-way-down/  译者:杨晓伟(采用意译)

你可以从这里从头阅读这个系列。

介绍

回忆下第10部分中的客户端5.1版。客户端使用一个Deferred来管理所有的回调链,其中包括一个格式转换引擎的调用。在那个版本中,这个引擎的实现是同步的。(即等待其执行再切到其它函数或任务中)

现在我们想实现一个新的客户端,其使用我们在第十二部分实现的格式服务器提供的格式转换服务。但这里有一个问题需要说清楚:由于格式转换服务是通过网络获取的,因此我们需要使用异步I/O。这也就意味着我们获取格式转换服务的API必须是异步实现的。换句话说,try_to_cummingsify回调将会在新客户端中返回一个deferred。

如果在一个deferred的回调链中的一个回函数又返回了一个 deferred会发生什么现象呢?我们规定前一个deferred为外层deferred,而后者则为内层deferred。假设回调N在外层deferred中返回一个内层的deferred。意味着这个回调宣称“我是一个异步函数,结果不会立即出现!”。由于外层的deferred需要调用回调链中下一个callback或errback并将回调N的结果传下去,因此,其必须等待直到内层deferred被激活。当然了,外层的deferred不可能处于阻塞状态,因为控制权此时已经转交给了reactor并且阻塞了。

那么外层的deferred如何知晓何时恢复执行呢?很简单,在内层deferred上添加callback或errback即可(即激活内层的deferred)。因此,当内层deferrd被激活时,外层的deferred恢复其回调链的执行。当内层deferred回调执行成功,那么外层deferred会调用第N+1个callback回调。相反,如果内层deferred执行失败,那么外层deferred会调用第N+1个errback回调。

图28形象地解释说明了这一过程:

图28 内层与外层deferred的交互

在这个图示中,外层的deferred有四个callback/errback对。当外围的deferred被激活后,其第一个callback回调返回了一个deferred(即内层deferred)。从这里开始,外层的deferred停止激活其回调链并且将控制权交还给了reactor(当然是在给内层deferred添加callback/errback之后)。过了一段时间之后,内层deferred被激活,然后执行它的回调链并执行完毕后恢复外层deferred的回调执行过程。注意到,外层deferred是无法激活内层deferred的。这是不可能的,因为外层的deferred根本就无法获知内层的deferred何时能把结果准备好及结果内容是什么。相反,外层的deferred只可能等待(当然是异步方式)内部deferred的激活。

注意到外层deferred的产生内层deferred的回调的连线是黑色的而不是红色或蓝色,这是因为我们在内层deferred激活之前是无法获知此回调返回的结果是执行成功还执行失败。只有在内层deferred激活时,我们才能决定下一个回调是callback还是errback。

图29从reactor的角度来说明了外层与内层deferred的执行序列:

图29 控制权的转换

这也许是Deferred类最为复杂的功能,但无需担心你可能会花费大量时间来理解它。我们将在示例twisted-deferred/defer-10.py中说明如何使用它。这个例子中,我们创建了两个外层deferred,一个使用了简单的回调,另一个其中的一个回调返回了一个内部deferred。通过阅读这段代码,我们可以发现外层deferred是在内层deferred激活后才开始继续执行回调链的。

客户端版本6.0

我们将使用新学的deferred嵌套来重写我们的客户端来使用由服务器提供的样式转换服务。其实现代码在

twisted-client-6/get-poetry.py中。与前几个版本一样,协议与工厂都没有改变。但我们添加了进行格式转换服务请求的协议与工厂实现。下面是协议实现代码:

class TransformClientProtocol(NetstringReceiver):
def connectionMade(self):
self.sendRequest(self.factory.xform_name, self.factory.poem) def sendRequest(self, xform_name, poem):
self.sendString(xform_name + '.' + poem) def stringReceived(self, s):
self.transport.loseConnection()
self.poemReceived(s) def poemReceived(self, poem):
self.factory.handlePoem(poem)

使用NetstringReceiver作为基类可以很简单地实现我们的协议。只要连接一旦建立我们就发出格式转换服务的请求。当我们得到格式转换之后的诗歌后交给工厂进行处理,下面是工厂代码:

class TransformClientFactory(ClientFactory):
protocol = TransformClientProtocol def __init__(self, xform_name, poem):
self.xform_name = xform_name
self.poem = poem
self.deferred = defer.Deferred() def handlePoem(self, poem):
d, self.deferred = self.deferred, None
d.callback(poem) def clientConnectionLost(self, _, reason):
if self.deferred is not None:
d, self.deferred = self.deferred, None
d.errback(reason) clientConnectionFailed = clientConnectionLost

值得注意的是,工厂是如何处理两种类型错误:连接失败与在诗歌未全部接收完就中断连接。并且clientConncetionLost可能会在我们已经接收完诗歌后激活执行(即连接断开了),但在这种情况下,self.deferred已经是个None值,这利益于handePoem中对deferredr 处理。

这个工厂创建了一个deferred并且最后激活了它,这在Twisted编程中是一个好的习惯,即

通常情况下,一个对象创建了一个deferred,那么它应当负责激活它。

除了格式转换工厂外,还有一个Proxy类开包装了具体创建一个TCP连接到格式转换服务器:

class TransformProxy(object):
"""
I proxy requests to a transformation service. """
def __init__(self, host, port):
self.host = host
self.port = port def xform(self, xform_name, poem):
factory = TransformClientFactory(xform_name, poem)
from twisted.internet import reactor
reactor.connectTCP(self.host, self.port, factory)
return factory.deferred

这个类提供了一个xform接口,以让其它程序请求格式转换服务。这样一来其它代码只需要提出请求并得到一个deferred,而无需考虑什么端口与IP地址之类的问题。

剩下的代码除了try_to_cummingsify外都没有改变:

def try_to_cummingsify(poem):
d = proxy.xform('cummingsify', poem)
def fail(err):
print >>sys.stderr, 'Cummingsify failed!'
return poem
return d.addErrback(fail)

这个作为外层deferred的回调返回了一个内层的deferred,但我们仍然需要更改main方法,除了创建了一个Proxy对象。由于try_to_cummingsify已经是deferred回调链中的一部分,因此其早已使用了异步方式。因此我们说main函数无需更改。

你可能注意到return d.addErrback(fail)这句,其它它等于

d.addErrback(fail)
return d

结束语

这一部分我们学习了关于deferred如何透明地完成了回调链内部再次处理deferred。并由此,我们可以无需考虑内部实现细节并放心地在外部deferred上添加回调。

在第十四部分,我们将讲解deferred的另外一个特性。

Python Twisted系列教程13:使用Deferred新功能实现新客户端的更多相关文章

  1. Python Twisted系列教程14:Deferred用于同步环境

    作者:dave@http://krondo.com/when-a-deferred-isnt/  译者:杨晓伟(采用意译) 你可以从这里从头开始阅读这个系列. 介绍 这部分我们要介绍Deferred的 ...

  2. Python Twisted系列教程10:增强defer功能的客户端

    作者:dave@http://krondo.com/an-introduction-to-asynchronous-programming-and-twisted/ 译者:杨晓伟(采用意译) 可以从这 ...

  3. Python Twisted系列教程17:造”回调”的另一种方法

    作者:dave@http://krondo.com/just-another-way-to-spell-callback/  译者: Cheng Luo 你可以从”第一部分 Twist理论基础“开始阅 ...

  4. Python Twisted系列教程19:改变之前的想法

    作者:dave@http://krondo.com/i-thought-i-wanted-it-but-i-changed-my-mind/  译者: Cheng Luo 你可以从”第一部分 Twis ...

  5. Python Twisted系列教程8:使用Deferred的诗歌下载客户端

    作者:dave@http://krondo.com/deferred-poetry/  译者:杨晓伟(采用意译) 可以从这里从头开始阅读这个系列. 客户端4.0 我们已经对deferreds有些理解了 ...

  6. Python Twisted系列教程12:改进诗歌下载服务器

    作者:dave@http://krondo.com/a-poetry-transformation-server/  译者:杨晓伟(采用意译) 你可以从这里从头阅读这个系列. 新的服务器实现 这里我们 ...

  7. Python Twisted系列教程16:Twisted 进程守护

    作者:dave@http://krondo.com/twisted-daemonologie/  译者: Cheng Luo 你可以从”第一部分 Twist理论基础“开始阅读:也可以从”Twisted ...

  8. Python Twisted系列教程5:由Twisted支持的诗歌客户端

    作者:dave@http://krondo.com/twistier-poetry/  译者:杨晓伟(采用意译) 你可以从这里从头开始阅读这个系列 抽象地构建客户端 在第四部分中,我们构建了第一个使用 ...

  9. Python Twisted系列教程4:由Twisted支持的诗歌客户端

    作者:dave@http://krondo.com/twisted-poetry/  译者:杨晓伟(采用意译) 你可以在这里从头开始阅读这个系列. 第一个twisted支持的诗歌服务器 尽管Twist ...

随机推荐

  1. Solr快速入门

    1. 什么是Solr Solr是基于lucene的全文检索服务器.不同于lucene工具包,solr是一个web应用,运行在servlet容器,屏蔽了底层细节,并对外提供服务. 点我lucene快速入 ...

  2. 第七次scrum meeting记录

    文章负责:张华杰 日期:2017年10月31日 会议地点:主楼主南201 各组员工作情况 团队成员 昨日完成任务 明日要完成任务 赵晓宇 课程列表页面搭建 issue20 课程列表页面搭建(part ...

  3. Selenium2+Python自动化学习笔记(第1天)

    参考[http://blog.csdn.net/henni_719/article/details/51096531]大神写的笔记,多谢大神共享. 哈哈,今天又找到一位大神写的Selenium2+Py ...

  4. 利用 localStorage 储存css js

    链接  版本号, 可以后台输出到jsp页面上 移动端webapp值得一试: 兼容性好 网速慢,LS读取+eval大多数情况下快于304 webapp不需要seo,css也可以缓存,再通过js加载 浏览 ...

  5. xhtml html

    xhtml是用xml语言重写了html,相比html更规范了, XHTML是HTML像XML的一个过渡语言,它比HTML严谨性会高点,然后基本语言都还是沿用的HTML的标签,只不过废除了部分表现层的标 ...

  6. Ubuntu語言支持爲灰色修復方法

    Ubuntu語言支持爲灰色修復方法 在Ubuntu12.04中,在下不知爲何將 語言支持 中 應用到整個系統 和 添加語言 這2個按弄成了灰色,導致ibus不能輸入中文,現在唔將修復方法公告天下: 1 ...

  7. 视图框架:Spring MVC 4.0(2)

    在<springMVC4(7)模型视图方法源码综合分析>一文中,我们介绍了ModelAndView的用法,它会在控制层方法调用完毕后作为返回值返回,里面封装好了我们的业务逻辑数据和视图对象 ...

  8. MFC多语言程序版本,在不同的windows系统上的使用 FP_SetThreadUILanguage

    from: http://www.cnblogs.com/qijicxl/p/3840157.html 如何使MFC程序界面支持多国语言?这次使用后给自己做一个总结. 我们使用vc6.0的版本来试验 ...

  9. Git克隆、修改、更新项目,及查看项目地址命令

    第一步:在本地新建一个文件夹,作为本地仓库,如“texzt”,直接打开该文件夹,并单击右键,选择git bash here 则可以直接进入到该文件夹目录下. 第二步:将本地仓库初始化,命令:git i ...

  10. python RabbitMQ队列使用

    python RabbitMQ队列使用 关于python的queue介绍 关于python的队列,内置的有两种,一种是线程queue,另一种是进程queue,但是这两种queue都是只能在同一个进程下 ...