twisted系列教程十四— pre-fireed deferred
Introduction
在这一部分我们将要学习deferred 类的另外的一个方面.为了促进讨论,我们要为我们的poetry service增加一个server.假设我们有大量的内部的client 想要连接一个相同的外部的server.假设这个server已经很慢而且已经负载很高了.我们不想再让server上连接更多的client 了.
所以我们会创建一个缓存代理服务器.当一个client 连接到proxy的时候,这个proxy或者从外部的server获取到一首诗或者就返回一个之前已经缓存了的内容.我们可以让我们的client 都连接proxy,我们的外部的server 的负载就会很小.我们可以用图片三十来描述这个过程:
图片三十
思考一下当一个client连接到proxy 之后会发生什么,假如这个proxy 的缓存是不存在的,这个proxy必须异步等待外部的server 返回一个结果然后才能返回client.到目前为止还不错,我们已经知道怎样去处理返回deferred 的异步的函数.另一方面,假如在缓存中已经有了一个一首诗,这个proxy 会把它立即返回,一点也不用等待.所以proxy获取一首诗的内容可以是同步的或者是异步的.
所以 我们能做些什么假如我们有一个有时异步有时同步的函数?twisted 提供了很多选项,并且它们依据deferred 的一个我们没有讲的特色:你可以在你返回deferred之前触发它.
这个是管用的,因为尽管你不能触发一个deferred 两次,但是你可以在deferred 触发之后向deferred 中增加callbacks 和 errbacks.当你这样做的时候,deferred 会继续的触发 callback/errback 链 从上次它离开的地方.一个很重要的一点是一个已经触发的deferred 可以立即触发新假如的callback.
图片三十一展示了一个已经被触发的deferred:
图片三十一
如果我们现在向其中加入一对callback/errback,这个deferred 会立即的触发新加入的callback,就像图片三十二:
图片三十二
我们测试这个deferred 的新特色通过代码twisted-deferred/defer-11.py.试着运行一下并看看deferred 被触发之后又加入callback 之后会发生什么.注意在第一个例子中每个新的callback是怎样被立即触发的.
第二个例子展示了我们怎样pause() 一个deferred不让它立即触发callback的.当我们都准备好的时候,我们可以用unpause().其实暂停deferred 的原理和 当一个callback 返回deferred 导致外部的deferred 暂停的原理是一样的.
Proxy 1.0
现在让我们看一下第一版的poetry proxy –twisted-server-1/poetry-proxy.py,因为这个proxy 同时扮演了client 和 server 的角色,它有两对 Protocol/Factory.一个用来为poetry 服务,另一个用来从外部的server 获取诗歌的内容.我们就不看为client 服务的protocol/factory 了,和以前的版本一样.
让我们来看 ProxyService,proxy中的server-side protocol 利用它来从外部的server获取一首诗:
class ProxyService(object):
poem = None # the cached poem
def __init__(self, host, port):
self.host = host
self.port = port
def get_poem(self):
if self.poem is not None:
print 'Using cached poem.'
return self.poem
print 'Fetching poem from server.'
factory = PoetryClientFactory()
factory.deferred.addCallback(self.set_poem)
from twisted.internet import reactor
reactor.connectTCP(self.host, self.port, factory)
return factory.deferred
def set_poem(self, poem):
self.poem = poem
return poem
重要的方法是get_poem.假如在缓存里已经有一首诗存在,直接返回.如果没有的话,我们向外部的server 发起一个连接并返回一个deferred,如果等待的诗来到则触发deferred.get_poem 是一个只有一部分时间是异步的.
怎样来处理那样的一个函数呢?让我们来看一下server-side protocol/factory :
class PoetryProxyProtocol(Protocol):
def connectionMade(self):
d = maybeDeferred(self.factory.service.get_poem)
d.addCallback(self.transport.write)
d.addBoth(lambda r: self.transport.loseConnection())
class PoetryProxyFactory(ServerFactory):
protocol = PoetryProxyProtocol
def __init__(self, service):
self.service = service
这个factory 是很简单的,它只保存了一个proxy service 的引用,这样可以让protocol 实例可以调用get_poem 方法.protocol 是核心所在.它没有直接的调用get_poem,而是使用了一个twisted.internet.defer 的封装—maybeDeferred.
maybeDeferred 函数拿到一个函数的引用,并加上了一些参数,maybeDeferred 会最终调用这个函数,并且做如下的工作:
如果这个函数返回了一个deferred,maybeDeferred 也返回这个deferred,或者
假如这个函数返回了一个Failure,maybeDeferred 返回一个已经被触发的deferred ,并带着failure参数,或者
假如这个函数返回了一个正常的值,maybeDeferred返回一个已经被触发的deferred,并带着这个正常的值作为参数,或者
假如这个函数抛出了一个错误,maybeDeferred会返回一个已经被触发的deferred,并带着由这个错误转化来的failure作为参数
换句话说,从maybeDeferred 返回的值一定是一个deferred,即使你传递过去的函数不会返回deferred.这就让我们可以安全的调用一个同步的函数,并把它当作一个返回deferred异步的函数.
注意一:这里仍有一点不一样,被一个同步的函数返回的deferred 是已经被触发过的,所以任何的你加入的callback 和errback 都会被立即调用,而不是在一些reactor loop 的迭带之后.
注意二:也许给一个一定会返回deferred 的函数命名为maybeDeferred 不是一个特别好的选择.
一但这个protocol 有了一个真正的deferred,它可以增加一些callback把诗送到client,并关闭相应的连接.这个就是我们的第一个poetry proxy.
Running the Proxy
要测试我们的代理的话,先开启一个poetry server,像下面这样:
python twisted-server-1/fastpoetry.py --port 10001 poetry/fascination.txt
然后开启一个proxy server:
python twisted-server-1/poetry-proxy.py --port 10000 10001
也就说proxy 运行在10000端口,poetry server 运行在10001端口.
下面你可以运行一个client 连接proxy:
python twisted-client-4/get-poetry.py 10000
我们使用了一个早期的没有poetry transformations 的client 版本.你可以看到一首诗出现在client 的窗口里,还有一些文字说明它正在从server 下载.如果你再运行client 一次,这个proxy 会告诉你它正在使用缓存起来的poem.
Proxy 2.0
我们前面已经说过,还有另外一种方法可以实现我们的需求.在Porxy 2.0 中有说明,代码见twisted-server-2/poetry-proxy.py.既然我们可以在返回deferred之前触发它,我们可以让proxy service 在缓存中已经存在这首诗的时候返回一个已经触发过的deferred.下面是proxy service 中get_poem 的新版本:
def get_poem(self):
if self.poem is not None:
print 'Using cached poem.'
# return an already-fired deferred
return succeed(self.poem)
print 'Fetching poem from server.'
factory = PoetryClientFactory()
factory.deferred.addCallback(self.set_poem)
from twisted.internet import reactor
reactor.connectTCP(self.host, self.port, factory)
return factory.deferred
这个defer.succeed 函数是创建一个已经触发的deferred并返回一个值的很便捷的方法.查看一下它的实现你会发现它就是创建一个deferred ,并用callback()触发 的封装.如果我们想返回一个已经失败了的deferred 我们可以用defer.fail.
在这个版本中,因为get_poem 已经返回了一个deferred,protocol 类不再需要maybeDeferred:
class PoetryProxyProtocol(Protocol):
def connectionMade(self):
d = self.factory.service.get_poem()
d.addCallback(self.transport.write)
d.addBoth(lambda r: self.transport.loseConnection())
除了这两个地方的变化之外,其他的没什么变化了.你可以像上面的方法一样来运行它.
Summary
在这一部分我们学到了怎样deferred 在被返回之前被调用,因而我们可以在同步的程序使用它,我们有两种方法去实现它:
我们可以使用maybeDeferred来处理时而返回deferred 时而返回正常结果的函数
我们可以用defer.succed 和 defer.fail 提前触发我们的deferred,所以我们的有时同步有时异步的函数可以总是返回deferred
我们可以使用他们中的任何一个方法.前一个强调了我们的函数不是总是异步的,而后一个让代码更简单.没有一个定论非要使用哪个.
两种方法都可以是因为我们可以向deferred中增加callback/errback 在它被触发之后.它也解了我们在第九部分提出的疑问.我们了解到在deferred中,不管是最后一个callback 或者errback 失败,错误会在deferred 被垃圾回收的时候才被报告出来.现在我们知道因为什么了–因为我们可以一直向一个deferred 对象中增加一个callback/errback 对,直到最后一个对deferred 的引用也消失了,twisted 才能认定这个错误没有被处理.
所以,这就是deferred了吗?我们已经知道deferred 的全部了吗? 对于大部分来说,是的.但是twisted 包含了很多我们还没有探寻到的很多种交替使用deferred 的方式.同时,twisted 的开发人员也在不停的增加新的特色.在将来的发布的版本中,deferred 会有更多的能力.我们会在以后的章节中讲到,但首先我们需要从deferred中休息一下,看一些twisted 的其他的方面.
----
20120821 16:08
运行 Proxy 1.0 是做为一个代理服务器,客户端是连接到这个 Proxy 1.0 上而不是外部的 server 上。如果 Proxy 1.0 中有缓存数据的话,将取到的数据通过 PretryProxyProtocol 中的 d.addCallback(self.transport.write) 发给先前的客户端。否则向外部的 server 发起一个连接用于取数据,再通过同样的方式发送给客户端。
相关文章
- Unity3D脚本中文系列教程(十四)
- Unity3D脚本中文系列教程(十一)
- 手机便签设置按数字序号排序教程?
- ansj分词史上最详细教程
- vagrant系列教程(一):vagrant的安装与初识(转)
- xpath教程 1 - 什么是XPath
- Python视觉深度学习系列教程 第三卷 第4章 在ImageNet上训练AlexNet
- Python视觉深度学习系列教程 第二卷 第1章 数据增强
- Python视觉深度学习系列教程 第一卷 第14章 使用LeNet识别手写数字
- HTML DOM 教程Part1
- 微信程序开发系列教程(四)使用微信API创建公众号自定义菜单
- 版本控制入门插图教程
- Atitit.播放系统的选片服务器,包厢记时系统 的说明,教程,维护,故障排查手册p825
- 微信程序开发系列教程(二)使用JavaScript给微信用户发送消息
- SAP系统和微信集成的系列教程之四:如何将SAP C4C主数据变化推送给微信公众号的关注者
- 【Mac 教程系列】Mac 实用命令大全
- 2022最新版JDK1.8的安装教程、包含jdk1.8的提取码(亲测可用)
- Python+Django+SAE系列教程15-----输出非HTML内容(图片/PDF)
- [Unity官方文档翻译]Primitive and Placeholder Objects Unity原生3D物体教程
- Cocos2d-x 3.0final 终结者系列教程16-《微信飞机大战》实现
- 学习笔记 | Linux系列教程之文本三剑客 - awk、grep、sed从入门到实战
- 图解iPhone开发新手教程
- 一分钟制作U盘版BT3 - 有图滴儿 bt3破解教程
- YOLO算法创新改进系列项目汇总(入门级教程指南)
- 通过 docker-compose 快速部署 Hadoop 集群详细教程
- twisted系列教程十二–为server 增加一个service
- 从零开始手写Tomcat的教程14节----服务器组件(Server)和服务组件(Service)
- ECharts 饼状图颜色设置教程 - 4 种方式设置饼图颜色
- 【Git技巧】第九篇 git分支操作手把手教程(亲测演练)