(轉) Twisted : 第十四部分 Deferred用於同步環境

介紹python

這部分咱們要介紹Deferred的另一個功能。便於討論,咱們設定以下情景:假設因爲衆多的內部網請求一個外部詩歌下載服務器,但因爲這個外部下載服務器性能太差或請求負荷過重。所以,咱們不想將全部的內部請求所有發送到外部服務器。咱們的處理辦法是,在中間添加一個緩存代理。當一個請求來到後,咱們能夠從中間緩存代理中找到緩存的備份(若是有緩存)或者直接從外部服務器得到。部署圖如圖30所示:react

第十四部分 <wbr>Deferred用於同步環境

30 緩存代理服務器git

考慮到,客戶端端發送請求來時,此緩存代理可能會將本地的緩衝的詩歌取出並回復,也有可能須要異步等待外部詩歌下載服務器的詩歌回覆。如此一來,就會出現這樣的情景:客戶端發送來的請求,緩存代理處理請求多是同步也多是異步。github

要解決這個須要,就用到了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的其它內容。

相關文章
相關標籤/搜索