(轉) Twisted : 第十三部分 使用Deferred新功能實現新客戶端

介紹python

回憶下第10部分中的客戶端5.1版。客戶端使用一個Deferred來管理全部的回調鏈,其中包括一個格式轉換引擎的調用。在那個版本中,這個引擎的實現是同步的。(即等待其執行再切到其它函數或任務中)react

如今咱們想實現一個新的客戶端,其使用咱們在第十二部分實現的格式服務器提供的格式轉換服務。但這裏有一個問題須要說清楚:因爲格式轉換服務是經過網絡獲取的,所以咱們須要使用異步I/O。這也就意味着咱們獲取格式轉換服務的API必須是異步實現的。換句話說,try_to_cummingsify回調將會在新客戶端中返回一個deferredgit

若是在一個deferred的回調鏈中的一個回函數又返回了一個 deferred會發生什麼現象呢?咱們規定前一個deferred爲外層deferred,然後者則爲內層deferred。假設回調N在外層deferred中返回一個內層的deferred。意味着這個回調宣稱「我是一個異步函數,結果不會當即出現!」。因爲外層的deferred須要調用回調鏈中下一個callbackerrback並將回調N的結果傳下去,所以,其必須等待直到內層deferred被激活。固然了,外層的deferred不可能處於阻塞狀態,由於控制權此時已經轉交給了reactor而且阻塞了。github

那麼外層的deferred如何知曉什麼時候恢復執行呢?很簡單,在內層deferred上添加callbackerrback便可(即激活內層的deferred)。所以,當內層deferrd被激活時,外層的deferred恢復其回調鏈的執行。當內層deferred回調執行成功,那麼外層deferred會調用第N+1callback回調。相反,若是內層deferred執行失敗,那麼外層deferred會調用第N+1errback回調。編程

28形象地解釋說明了這一過程:服務器

第十三部分 <wbr>使用Deferred新功能實現新客戶端

28 內層與外層deferred的交互網絡

在這個圖示中,外層的deferred有四個callback/errback對。當外圍的deferred被激活後,其第一個callback回調返回了一個deferred(即內層deferred)。從這裏開始,外層的deferred中止激活其回調鏈而且將控制權交還給了reactor(固然是在給內層deferred添加callback/errback以後)。過了一段時間以後,內層deferred被激活,而後執行它的回調鏈並執行完畢後恢復外層deferred的回調執行過程。注意到,外層deferred是沒法激活內層deferred的。這是不可能的,由於外層的deferred根本就沒法獲知內層的deferred什麼時候能把結果準備好及結果內容是什麼。相反,外層的deferred只可能等待(固然是異步方式)內部deferred的激活。異步

注意到外層deferred的產生內層deferred的回調的連線是黑色的而不是紅色或藍色,這是由於咱們在內層deferred激活以前是沒法獲知此回調返回的結果是執行成功還執行失敗。只有在內層deferred激活時,咱們才能決定下一個回調是callback仍是errback函數

29reactor的角度來講明瞭外層與內層deferred的執行序列:學習

第十三部分 <wbr>使用Deferred新功能實現新客戶端

29 控制權的轉換

 

這也許是Deferred類最爲複雜的功能,但無需擔憂你可能會花費大量時間來理解它。咱們將在示例twisted-deferred/defer-10.py中說明如何使用它。這個例子中,咱們建立了兩個外層deferred,一個使用了簡單的回調,另外一個其中的一個回調返回了一個內部deferred。經過閱讀這段代碼,咱們能夠發現外層deferred是在內層deferred激活後纔開始繼續執行回調鏈的。


客戶端版本6.0

咱們將使用新學的deferred嵌套來重寫咱們的客戶端來使用由服務器提供的樣式轉換服務。其實現代碼在

twisted-client-6/get-poetry.py中。與前幾個版本同樣,協議與工廠都沒有改變。但咱們添加了進行格式轉換服務請求的協議與工廠實現。下面是協議實現代碼:

class TransformClientProtocol(NetstringReceiver):
    def connectionMade(self):
        self.sendRequest(self.factory.xform_name, self.factory.poem)
    def sendRequest(self, xform_name, poem):
        self.sendString(xform_name + '.' + poem)
    def stringReceived(self, s):
        self.transport.loseConnection()
        self.poemReceived(s)
    def poemReceived(self, poem):
        self.factory.handlePoem(poem)


使用NetstringReceiver做爲基類能夠很簡單地實現咱們的協議。只要鏈接一旦創建咱們就發出格式轉換服務的請求。當咱們獲得格式轉換以後的詩歌后交給工廠進行處理,下面是工廠代碼:

class TransformClientFactory(ClientFactory):
    protocol = TransformClientProtocol
    def __init__(self, xform_name, poem):
        self.xform_name = xform_name
        self.poem = poem
        self.deferred = defer.Deferred()
    def handlePoem(self, poem):
        d, self.deferred = self.deferred, None
        d.callback(poem)
    def clientConnectionLost(self, _, reason):
        if self.deferred is not None:
            d, self.deferred = self.deferred, None
            d.errback(reason)
    clientConnectionFailed = clientConnectionLost


值得注意的是,工廠是如何處理兩種類型錯誤:鏈接失敗與在詩歌未所有接收完就中斷鏈接。而且clientConncetionLost可能會在咱們已經接收完詩歌后激活執行(即鏈接斷開了),但在這種狀況下,self.deferred已是個None值,這利益於handePoem中對deferredr 處理。

這個工廠建立了一個deferred而且最後激活了它,這在Twisted編程中是一個好的習慣,即

一般狀況下,一個對象建立了一個deferred,那麼它應當負責激活它。

除了格式轉換工廠外,還有一個Proxy類開包裝了具體建立一個TCP鏈接到格式轉換服務器:

class TransformProxy(object):
    """
    I proxy requests to a transformation service.
    """
    def __init__(self, host, port):
        self.host = host
        self.port = port
    def xform(self, xform_name, poem):
        factory = TransformClientFactory(xform_name, poem)
        from twisted.internet import reactor
        reactor.connectTCP(self.host, self.port, factory)
        return factory.deferred


這個類提供了一個xform接口,以讓其它程序請求格式轉換服務。這樣一來其它代碼只須要提出請求並獲得一個deferred,而無需考慮什麼端口與IP地址之類的問題。

剩下的代碼除了try_to_cummingsify外都沒有改變:

def try_to_cummingsify(poem):
    d = proxy.xform('cummingsify', poem)
    def fail(err):
        print >>sys.stderr, 'Cummingsify failed!'
        return poem
    return d.addErrback(fail)


這個做爲外層deferred的回調返回了一個內層的deferred,但咱們仍然須要更改main方法,除了建立了一個Proxy對象。因爲try_to_cummingsify已是deferred回調鏈中的一部分,所以其早已使用了異步方式。所以咱們說main函數無需更改。

你可能注意到return d.addErrback(fail)這句,其它它等於

d.addErrback(fail)
return d

結束語

這一部分咱們學習了關於deferred如何透明地完成了回調鏈內部再次處理deferred。並由此,咱們能夠無需考慮內部實現細節並放心地在外部deferred上添加回調。

在第十四部分,咱們將講解deferred的另一個特性。

相關文章
相關標籤/搜索