twisted系列教程十三–deferred 中的deferred
Introduction
回想一下第十部分的poetry client 5.1,client 用一个deferred 来管理一个callback 链,这个callback 链中调用了一个transformation 引擎,在client 5.1 中,这个引擎是作为一个同步的函数来实现的.
现在我们想写一个新的client,让它利用我们在第十二部分写的transformation service.问题就来了:既然transformation service 是通过网络访问的,我们需要用异步的I/O.这就意味着我们用来请求transformation 的api 是异步的.也就是说我们的try_to_cummingsify callback 会返回一个Deferred 对象
在一个deferred 链中的callback 返回另一个deferred 的时候会发生什么?让我们把第一个deferred 叫做外部的deferred 第二个deferred 叫做内部的deferred.假设在外部的deferred 中callback N 返回了 内部的deferred.这个callback 是在说”我是异步的,我的结果还没有来”.因为外部的deferred 需要调用下一个callback 或者errback,并传递当前callback 的返回值,外部的deferred 需要等待内部的deferred被触发.当然,外部的deferred 也不能是阻塞的,所以,此时外部的deferred暂定callback 链的执行并把控制圈交还给reactor.
外部的deferred 是怎样知道什么时候恢复呢? 很简单,通过给内部的deferred增加一个callback/errback 对.当内部的deferred 被触发的时候,外部的deferred 会恢复执行.假如内部的deferred成功了(例如:它调用了一个被外部的deferred增加的callback),外部的deferred则会继续调用它的N+1callback,假如内部的deferred 失败了,外部的deferred 会调用它的 N+1 errback.
让我们用一张图片来描述这个过程,图片二十八:
图片二十吧
在这张图片中,外部的deferred 有四对callback/errback.当外部的deferred 被触发时,第一个callback 返回了一个deferred(内部的deferred).这时候外部的deferred会停止触发它的callback 链,并把控制权交给reactor(在给内部的deferred 的增加了一对callback/errback 之后).然后,一段时间之后,内部的deferred触发,外部的deferred 也开始恢复运行.注意,外部的deferred 并不会自己触发内部的deferred.那也是不可能的,因为外部的deferred 不会知道什么时候内部的deferred的结果是可用的,或者结果是什么.我们的外部的deferred就是异步的等待内部的deferred 触发.
注意连接callback 和内部的deferred 的那跟线是黑色的而不是绿色或红色.那是因为我们我不知这个callback 是成功还是失败知道内部的deferred触发.直到那时候外部的deferred 才能知道是去调用下一个callback 还是下一个errback.
图片二十九描述了相同的外部的/内部的deferred 触发顺序,不过是站在reactor 的角度:
图片二十九
这个可能是deferred 最难懂的部分,如果你在短时间内不能消化也不要着急.我们会用具体的程序举例说明–twisted-deferred/defer-10.py.这个例子创造了两个外部的deferred,一个带有空白的callbacks,另一个有一个callback 并返回一个内部的deferred.通过学习这个例子你可以搞明白第二个外部的deferred是怎样在内部的deferred 返回的时候停止的,和 在内部的deferred被触发的时候外部的deferred 又是怎样恢复的.
Client 6.0
让我们用我们新学的嵌套的deferred 来重新实现一下我们的poetry client,并用上第十二部分讲到的transformation service,你可以在twisted-client-6/get-poetry.py 找到代码. poetry protocol 和protocol factory 和前一版本的client 都没有变化.但是增加了进行transformation 请求的protocol 和factory.下面是protocol 部分:
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 作为一个基类让这个protocol 相当的简单.只要连接一建立,我们从factory 中取到变形的名字和诗的内容并向server发送一个transform 请求.当我们得到返回的诗,我们把它传递给factory,下面是factory 的代码:
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
这个factory 是被client 设计的,处理一个transformation 请求,并存储着transform 的名字和这首诗的内容.factory 创造了一个代表了这个transformation请求返回结果的deferred.注意这个factory 是怎样处理两种错误情况的:一个是连接错误的情况一个是还没完全接受到返回值的时候连接就断开的情况.也注意clientConnectionLost 方法就算我们接受诗成功最后也会调用,但是在这种情况下self.deferred 已经被handlepoem 设置为None了.
这个factory 创建了一个deferred 也触发了它,这是一个很好的方法:
一般来说,一个创造了deferred 的对象,也应该负责触发那个deferred
这个”你创造它,你触发它”规则帮助我们保证一个deferred 仅仅被触发一次,也让程序流程更简单一些.
除了这个transform Factory,这里还有一个proxy 类,它隐藏连接transform server 的具体信息:
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()接口,其他的代码可以用它来发送transformations 请求.所以其他的代码就可以仅仅发送一个transformations 请求然后得到一个deferred,而不用再去关心ip 和端口号.
其他的代码变化的地方还有 try_to_cummingsify callback:
def try_to_cummingsify(poem):
d = proxy.xform('cummingsify', poem)
def fail(err):
print >>sys.stderr, 'Cummingsify failed!'
return poem
return d.addErrback(fail)
这个callback 现在返回一个deferred,但是我们不用改变其他的main 函数中的代码.因为try_to_cummingsify 本来就在deferred 的链中,它已经是异步的了,其他的就不用变化了.
你可能会发现我们返回的是d.addErrback(fail) 的结果,这里是用了一些语法糖.addCallback 和 addErrback 都返回原来的deferred.我们也可以写成:
d.addErrback(fail)
return d
Testing out the Client
这个新版的client 和其他的client相比有一些语法上的变化,假如你有一个transformation service 运行在10001 端口,两个poetry server 运行在10002 和 10003 上,你应该这样启动client:
python twisted-client-6/get-poetry.py 10001 10002 10003
你可以这样启动transformation service :
python twisted-server-1/transformedpoetry.py --port 10001
这样启动poetry server:
python twisted-server-1/fastpoetry.py --port 10002 poetry/fascination.txt
python twisted-server-1/fastpoetry.py --port 10003 poetry/science.txt
Wrapping Up
在这一部分我们学习了在一个callback 链中一个deferred 怎样透明的处理其他的deferred.我们可以安全的增加异步的callback到一个外部的deferred 中.这个是非常有用的因为我们的很多函数都要求是异步的.
我们知道deferred 的全部的事情了么? 还没有.还有很重要的一点,我们会在第十四部分讲到.
---
20120821 16:43 这一段比较重要:“
外部的deferred 是怎样知道什么时候恢复呢? 很简单,通过给内部的deferred增加一个callback/errback 对.当内部的deferred 被触发的时候,外部的deferred 会恢复执行.假如内部的deferred成功了(例如:它调用了一个被外部的deferred增加的callback),外部的deferred则会继续调用它的N+1callback,假如内部的deferred 失败了,外部的deferred 会调用它的 N+1 errback.”
twisted系列教程十三–deferred 中的deferred相关推荐
- PVE系列教程(十三)、安装黑苹果MacOS(Catalina版本)
PVE系列教程(十三).安装黑苹果MacOS(Catalina版本) 为了更好的浏览体验,欢迎光顾勤奋的凯尔森同学个人博客http://www.huerpu.cc:7000/.博客上有三个版本的镜像与 ...
- twisted系列教程十九–cancel deferred
Introduction twisted 是一个正在发展的项目,twisted 的开发者们会添加一些新的特色或者扩展旧的.随着twisted 10.1.0 的发布,开发者们增加了一个新的功能–取消,这 ...
- twisted系列教程十四— pre-fireed deferred
Introduction 在这一部分我们将要学习deferred 类的另外的一个方面.为了促进讨论,我们要为我们的poetry service增加一个server.假设我们有大量的内部的client ...
- twisted系列教程九–Deferred 的第二个小插曲
More Consequence of Callbacks 我们将要再来研究一下callback,尽管我们已经对deferred比较了解而且已经可以写出twisted 风格的异步程序,Deferred ...
- Python Twisted系列教程7:小插曲,Deferred
回调函数的后序发展 在第六部分我们认识这样一个情况:回调是Twisted异步编程中的基础.除了与reactor交互外,回调可以安插在任何我们写的Twisted结构内.因此在使用Twisted或其它基于 ...
- twisted系列教程八–延迟的诗
Client 4.0 既然我们已经对deferred有些了解了,我们可以用deferred 来重写我们的poetry client,你可以在这里找到client 4.0 twisted-client- ...
- twisted系列教程十七–用inlineCallbacks来管理callbacks
Introduction 在这一部分我们继续回到callback.我们将介绍用生成器来写callbacks.我们会讲到这个技巧怎么工作的,还有它和Deferred 的比较.最后我们会用这个技巧重写我们 ...
- twisted系列教程十–可以变化的诗
Client 5.0 现在我们将要想我们的client中加入一些变形逻辑.但是首先我不得不说:我不知道怎样写一个Byronification 引擎,它超出我的能力范围了.做为替代,我会实现一个相对简单 ...
- 【C++】C/C++系列教程汇总(更新中......)
文章目录 01. C语言基础 02. C开发实战 03. 数据结构 04. C++语言基础 05. C++核心编程 06. C++开发实战 07. MFC 08. QT 09. Visual Stud ...
最新文章
- Python:从零搭建Redis-Scrapy分布式爬虫
- A Comprehensive Analysis of Sequence Alignment Algorithms for LongRead Sequencing
- pycharm mysql安装_PyCharm安装连接MySQL
- 如何用面对对象来做一个躁动的小球?
- mybatis防止sql注入
- excel删除重复数据保留一条_Excel一键删除重复数据,你居然还用逐条排查?
- C/C++编程语言中操作目录及目录中文件的方法
- BZOJ 1597 [Usaco2008 Mar]土地购买 (斜率优化dp)
- PRML Chapter01 练习题Exercise
- VUE 下载文件流 文件无法打开,缺失数据
- 快速学习单反相机基础操作
- Week15 - 程序设计思维与实践 - 字符串算法
- python实现 猴子摘香蕉
- jQuery到Vue的迁移之路
- 几种常用的显示器分辨率
- 工程师小哥魔术揭秘“三仙归洞”,把我都看蒙了!
- java trove_[XMLer的生活]可使用基本类型作为键值的Java集合类-Trove 集合类
- 2022/4/18 天梯赛刷题记录2022天梯赛热身赛
- 物联网轻量级开发方案:在K3s部署Shifu,实现云边端闭环
- 神经网络架构搜索(Neural Architecture Search)杂谈