介绍python
这部分咱们要介绍Deferred的另一个功能。便于讨论,咱们设定以下情景:假设因为众多的内部网请求一个外部诗歌下载服务器,但因为这个外部下载服务器性能太差或请求负荷过重。所以,咱们不想将全部的内部请求所有发送到外部服务器。咱们的处理办法是,在中间添加一个缓存代理。当一个请求来到后,咱们能够从中间缓存代理中找到缓存的备份(若是有缓存)或者直接从外部服务器得到。部署图如图30所示:react
图30 缓存代理服务器git
考虑到,客户端端发送请求来时,此缓存代理可能会将本地的缓冲的诗歌取出并回复,也有可能须要异步等待外部诗歌下载服务器的诗歌回复。如此一来,就会出现这样的情景:客户端发送来的请求,缓存代理处理请求多是同步也多是异步。github
要解决这个须要,就用到了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的其它内容。