文档章节

(转) Twisted : 第十四部分 Deferred用于同步环境

水果糖
 水果糖
发布于 2016/01/27 13:23
字数 1620
阅读 14
收藏 0

介绍

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

第十四部分 <wbr>Deferred用于同步环境

30 缓存代理服务器

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

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

第十四部分 <wbr>Deferred用于同步环境

31 已经激活的deferred

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

第十四部分 <wbr>Deferred用于同步环境

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

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

我们可以通过 twisted-deferred/defer-11.py 示例来检测我们这里说到的特性。其中第二组例子,演示了deferred中的pauseunpause函数的功能,即可以暂停一个已经激活的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的其它内容。


本文转载自:http://blog.sina.com.cn/s/blog_704b6af70100py9n.html

共有 人打赏支持
水果糖
粉丝 15
博文 157
码字总数 52251
作品 0
深圳
程序员
(转) Twisted :第七部分 小插曲,Deferred

回调函数的后序发展 在第六部分我们认识这样一个情况: 回调是Twisted异步编程中的基础。除了与reactor交互外,回调可以安插在任何我们写的Twisted结构内。因此在使用Twisted或其它基于react...

水果糖
2016/01/27
14
0
(转) Twisted : 第八部分 使用Deferred的诗歌下载客户端

客户端4.0 我们已经对deferreds有些理解了,现在我们可以使用它重写我们的客户端。你可以在twisted-client-4/get-poetry.py中看到它的实现。 这里的getpoetry已经再也不需要callback与errba...

水果糖
2016/01/27
4
0
(转) Twisted : 第十三部分 使用Deferred新功能实现新客户端

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

水果糖
2016/01/27
13
0
(转) Twisted : 第九部分 第二个小插曲,Deferred

更多关于回调的知识 稍微停下来再思考一下回调的机制。尽管对于以Twisted方式使用Deferred写一个简单的异步程序已经非常了解了,但Deferred提供更多的是只有在比较复杂环境下才会用到的功能。...

水果糖
2016/01/27
7
0
twisted deferToThread使同步函数实现非阻塞

twisted的defer.Deferred (from twisted.internet import defer)可以返回一个deferred对象. 注:deferToThread使用线程实现的,不推荐过多使用 把同步函数变为异步(返回一个Deferred) twisted...

flyking
2013/09/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

10-《深度拆解JVM》JVM是怎么实现invokedynamic的?(下)

一、问题引入 上回讲到,为了让所有的动物都能参加赛马,Java 7 引入了 invokedynamic 机制,允许调用任意类的“赛跑”方法。不过,我们并没有讲解 invokedynamic,而是深入地探讨了它所依赖...

飞鱼说编程
15分钟前
0
0
457. Circular Array Loop

Description Difficulty : Medium You are given an array of positive and negative integers. If a number n at an index is positive, then move forward n steps. Conversely, if it's n......

52iSilence7
31分钟前
0
0
MySQL SQL 常见用法

某字段重复记录 select a.fieldA from tableA a group by a.fieldA having count(a.fieldA)>1;==select * from (select a.fieldA, count(1) as faCount from tableA a group......

园领T
今天
1
0
如何使用 iptables 和 firewalld 工具来管理 Linux 防火墙规则

以下是如何使用 iptables 和 firewalld 工具来管理 Linux 防火墙规则。 防火墙 防火墙是一组规则。当数据包进出受保护的网络区域时,进出内容(特别是关于其来源、目标和使用的协议等信息)会...

linuxprobe16
今天
1
0
发生系统错误 5 拒绝访问

1、使用命令net start *开启一个服务的时候,出现发生系统错误5,拒绝访问。 解决:切换到管理员模式执行此命令即可。

fang_faye
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部