作者:dave@http://krondo.com/when-a-deferred-isnt/  译者:杨晓伟(采用意译)

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

介绍

这部分我们要介绍Deferred的另外一个功能。便于讨论,我们设定如下情景:假设由于众多的内部网请求一个外部诗歌下载服务器,但由于这个外部下载服务器性能太差或请求负荷太重。因此,我们不想将所有的内部请求全部发送到外部服务器。我们的处理办法是,在中间添加一个缓存代理。当一个请求来到后,我们可以从中间缓存代理中找到缓存的备份(如果有缓存)或者直接从外部服务器获得。部署图如图30所示:

图30 缓存代理服务器

考虑到,客户端端发送请求来时,此缓存代理可能会将本地的缓冲的诗歌取出并回复,也有可能需要异步等待外部诗歌下载服务器的诗歌回复。如此一来,就会出现这样的情景:客户端发送来的请求,缓存代理处理请求可能是同步也可能是异步。

要解决这个需要,就用到了Deferred的另一个特性,即可以在将Deferred返回前就激活这个Deferred。之所以可以这样做,是因为你可以在一个已经激活的deferred上添加回调处理函数。一个非常值得注意的是:已经被激活的deferred可以立即激活新添加的回调处理函数。图31表示一个已经激活的deferred:

图31 已经激活的deferred

如果在此时,我们再为其另一对callback/errback,那么会立即激活新的回调并执行。如图32

图32 同一个deferred在添加新的回调之后

后面的callback回调被执行,是因为前面的callback执行成功。如果其执行失败,那么接下来执行的将是新添加的errback回调。

我们可以通过 twisted-deferred/defer-11.py 示例来检测我们这里说到的特性。其中第二组例子,演示了deferred中的pause与unpause函数的功能,即可以暂停一个已经激活的deferred对其回调链上回调的激活。并可以用unpause来解除暂停设置。这两个函数同样完成了在回调中继续产生deferred期间的控制。

代理 1.0版本

让我们来看看第一个版本的缓存代理的实现twisted-server-1/poetry-proxy.py。由于该服务器既作为服务器向客户端请求提供本地缓存的诗歌,同时也要作为向外部诗歌下载服务器提出下载请求的客户端。因此,其有两套协议/工厂,一套实现服务器角色,另一套实现客户端角色。

首先我们先来看看ProxyService的实现部分:

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。如果缓存中没有请求的诗歌,那么就会建立连接从外部服务器中异步取得而返回一个deferred,并将取得的诗歌放到缓冲区中。相反,若缓冲区中存在请求的诗歌,则直接返回诗歌。

我们如何来处理这样一个返回值不确定的函数呢,让我们来看看实现服务器角色的协议/工厂:

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

这里使用了maybeDeferred函数解决了这个问题。此函数的功能就是如果作为其参数返回值为defer,那么其不作任何处理,原样将defer返回。但如何返回值不是defer而是一个值(正如我们的缓存代理将本地缓冲的诗歌返回一样),那么这个maybeDeferred会将该值重新打包成一个已经激活的deferred返回,注意是已经激活的deferred。当然,如果返回的是一个异常,其也会将其打包成一个已经激活的deferred,只不过就不是通过callback而是errback激活的。

代理 2.0版本

前面我们已经提到,有另一种替代方法来实现这一机制。这在 twisted-server-2/poetry-proxy.py 中很好的说明了。即我们可以返回一个已经激活的defer,放在这儿就是如果缓存代理中有请求的诗歌,那么就通过返回一个激活的deferred

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

如果我们去看succeed的源码会发现,其只是在返回一个deferred之前,将其激活。同样,如果想要返回一个以失败的方式激活的deferred,可以调用函数defer.fail

在这个版本中,由于get_poem返回的是deferred而不像前一个版本存在不确定性因素。因此协议实现就无需使用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())

总结

这个部分我们学习到了deferred可以在返回之前被激活,这样我们就可以将其用于同步环境中。并且我们已经知道了有两种方法来实现。其一是使用maybeDeferred函数,其二是使用succeed/fail。两者返回的都是deferred,不同的是前者返回的可能是异步的也可能是同步的,而后者返回的肯定是同步的,即已经激活。

Deferred可以在激活后添加新的回调也间接说明了我们在第九部分提到的,deferred中会在最后一个回调中遇到未处理异常,并在此deferred被垃圾回收(即其已经没有任何外界引用)时才将该异常的情况打印出来。deferred回在其销毁前一直持有异常,等待可能还会添加进来的回调来处理。

我们已经将deferred中的大部分功能都介绍完了,当然Twisted开发人员可能不会增强deferred的功能。我们下一部分将讲讲Twisted的其它内容。

Python Twisted系列教程14:Deferred用于同步环境的更多相关文章

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

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

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

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

  3. Python Twisted系列教程7:小插曲,Deferred

    作者:dave@http://krondo.com/an-interlude-deferred/  译者:杨晓伟(采用意译) 你可以从这里从头开始阅读这个系列 回调函数的后序发展 在第六部分我们认识这 ...

  4. Python Twisted系列教程6:抽象地利用Twisted

    作者:dave@http://krondo.com/and-then-we-took-it-higher/  译者:杨晓伟(采用意译) 你可以从这里从头开始阅读这个系列. 打造可以复用的诗歌下载客户端 ...

  5. Python Twisted系列教程9:第二个小插曲,Deferred

    作者:dave@http://krondo.com/a-second-interlude-deferred/ 译者:杨晓伟(采用意译) 可以从这里从头来阅读这个系列 更多关于回调的知识 稍微停下来再思 ...

  6. Python Twisted系列教程13:使用Deferred新功能实现新客户端

    作者:dave@http://krondo.com/deferred-all-the-way-down/  译者:杨晓伟(采用意译) 你可以从这里从头阅读这个系列. 介绍 回忆下第10部分中的客户端5 ...

  7. Python Twisted系列教程1:Twisted理论基础

    作者:dave@http://krondo.com/in-which-we-begin-at-the-beginning/  译者:杨晓伟(采用意译) 前言: 最近有人在Twisted邮件列表中提出诸 ...

  8. Python Twisted系列教程21: Twisted和Haskell

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

  9. Python Twisted系列教程20: Twisted和Erlang

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

随机推荐

  1. Solr工具类

    import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.spr ...

  2. iOS-免证书真机调试

     使用方法: 1.新建一个普通的项目 2.进入xcode,菜单栏选择xcode –> preferences (快捷键 command + ,) 3.在Accounts选项卡添加自己的Apple ...

  3. 条款2:尽量以const enum inline 来替换 #define

    这里说的意思其实相当于,宁可以用编译器来替换预处理器 因为使用预处理器可能使得被处理过的东西无法进入符号表,例如 #define MAXLEN 16 这里的MAXLEN并没有进入符号表,这样有编译错误 ...

  4. 遍历输出所有子视图(View)

    传入一个View,可以获取传入视图的所有子视图,写入桌面,可以在火狐浏览器下查看 /** * 程序获得了焦点就会自动调用这个方法(只要程序获得了焦点,所有控件才能接收触摸事件) */ - (void) ...

  5. Unity在协程(Coroutines)内开启线程(Threading )

    孙广东  2017.6.13 http://blog.csdn.NET/u010019717 为什么要在协程中开启线程, 因为很多时候我们是需要线程执行完成后回到主线程的.然后主线程在继续执行后续的操 ...

  6. [置顶] Android 关于BottomDialogSheet 与Layout擦出爱的火花?

    今天上班做那个类似于ios拍照的那种效果图 就是个垂直布局然后里面textview+分割线+textview+button 当然也可以用button+分割线+button 方法有很多,选择适合自己的就 ...

  7. ng 自定义过滤器的创建和使用

    过滤器的本质就是一个方法,参数就是输入的值以及给过滤器指定的参数,返回值就是处理后要显示的值. ①过滤器创建var app = angular.module();app.filter(‘名称’,fun ...

  8. Delphi XE4 Upate1 更新升级记录.

    一直没时间,这两天折腾了一下 升级了.  其实也可能修了老bug 引入新bug. 呵呵. 看看Emb 都修了什么吧.  我干脆是重新安装的. 虽然官方也有一个单独的update.exe.  从这些bu ...

  9. SlowHTTPTest-慢速DoS攻击

    Slowhttptest是一个依赖于实际HTTP协议的Slow HTTP DoS攻击工具,它的设计原理是要求服务器所有请求被完全接收后再进行处理. SlowHTTPTest是一款对服务器进行慢攻击的测 ...

  10. InpOut32 CSharpExample.cs hacking

    /************************************************************************************ * InpOut32 CSh ...