twisted系列教程十四— pre-fireed deferred
Introduction
在這一部分我們將要學(xué)習(xí)deferred 類的另外的一個(gè)方面.為了促進(jìn)討論,我們要為我們的poetry service增加一個(gè)server.假設(shè)我們有大量的內(nèi)部的client 想要連接一個(gè)相同的外部的server.假設(shè)這個(gè)server已經(jīng)很慢而且已經(jīng)負(fù)載很高了.我們不想再讓server上連接更多的client 了.
所以我們會(huì)創(chuàng)建一個(gè)緩存代理服務(wù)器.當(dāng)一個(gè)client 連接到proxy的時(shí)候,這個(gè)proxy或者從外部的server獲取到一首詩或者就返回一個(gè)之前已經(jīng)緩存了的內(nèi)容.我們可以讓我們的client 都連接proxy,我們的外部的server 的負(fù)載就會(huì)很小.我們可以用圖片三十來描述這個(gè)過程:
圖片三十
思考一下當(dāng)一個(gè)client連接到proxy 之后會(huì)發(fā)生什么,假如這個(gè)proxy 的緩存是不存在的,這個(gè)proxy必須異步等待外部的server 返回一個(gè)結(jié)果然后才能返回client.到目前為止還不錯(cuò),我們已經(jīng)知道怎樣去處理返回deferred 的異步的函數(shù).另一方面,假如在緩存中已經(jīng)有了一個(gè)一首詩,這個(gè)proxy 會(huì)把它立即返回,一點(diǎn)也不用等待.所以proxy獲取一首詩的內(nèi)容可以是同步的或者是異步的.
所以 我們能做些什么假如我們有一個(gè)有時(shí)異步有時(shí)同步的函數(shù)?twisted 提供了很多選項(xiàng),并且它們依據(jù)deferred 的一個(gè)我們沒有講的特色:你可以在你返回deferred之前觸發(fā)它.
這個(gè)是管用的,因?yàn)楸M管你不能觸發(fā)一個(gè)deferred 兩次,但是你可以在deferred 觸發(fā)之后向deferred 中增加callbacks 和 errbacks.當(dāng)你這樣做的時(shí)候,deferred 會(huì)繼續(xù)的觸發(fā) callback/errback 鏈 從上次它離開的地方.一個(gè)很重要的一點(diǎn)是一個(gè)已經(jīng)觸發(fā)的deferred 可以立即觸發(fā)新假如的callback.
圖片三十一展示了一個(gè)已經(jīng)被觸發(fā)的deferred:
圖片三十一
如果我們現(xiàn)在向其中加入一對callback/errback,這個(gè)deferred 會(huì)立即的觸發(fā)新加入的callback,就像圖片三十二:
圖片三十二
我們測試這個(gè)deferred 的新特色通過代碼twisted-deferred/defer-11.py.試著運(yùn)行一下并看看deferred 被觸發(fā)之后又加入callback 之后會(huì)發(fā)生什么.注意在第一個(gè)例子中每個(gè)新的callback是怎樣被立即觸發(fā)的.
第二個(gè)例子展示了我們怎樣pause() 一個(gè)deferred不讓它立即觸發(fā)callback的.當(dāng)我們都準(zhǔn)備好的時(shí)候,我們可以用unpause().其實(shí)暫停deferred 的原理和 當(dāng)一個(gè)callback 返回deferred 導(dǎo)致外部的deferred 暫停的原理是一樣的.
Proxy 1.0
現(xiàn)在讓我們看一下第一版的poetry proxy –twisted-server-1/poetry-proxy.py,因?yàn)檫@個(gè)proxy 同時(shí)扮演了client 和 server 的角色,它有兩對 Protocol/Factory.一個(gè)用來為poetry 服務(wù),另一個(gè)用來從外部的server 獲取詩歌的內(nèi)容.我們就不看為client 服務(wù)的protocol/factory 了,和以前的版本一樣.
讓我們來看 ProxyService,proxy中的server-side protocol 利用它來從外部的server獲取一首詩:
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.假如在緩存里已經(jīng)有一首詩存在,直接返回.如果沒有的話,我們向外部的server 發(fā)起一個(gè)連接并返回一個(gè)deferred,如果等待的詩來到則觸發(fā)deferred.get_poem 是一個(gè)只有一部分時(shí)間是異步的.
怎樣來處理那樣的一個(gè)函數(shù)呢?讓我們來看一下server-side protocol/factory :
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
這個(gè)factory 是很簡單的,它只保存了一個(gè)proxy service 的引用,這樣可以讓protocol 實(shí)例可以調(diào)用get_poem 方法.protocol 是核心所在.它沒有直接的調(diào)用get_poem,而是使用了一個(gè)twisted.internet.defer 的封裝—maybeDeferred.
maybeDeferred 函數(shù)拿到一個(gè)函數(shù)的引用,并加上了一些參數(shù),maybeDeferred 會(huì)最終調(diào)用這個(gè)函數(shù),并且做如下的工作:
????如果這個(gè)函數(shù)返回了一個(gè)deferred,maybeDeferred 也返回這個(gè)deferred,或者
????假如這個(gè)函數(shù)返回了一個(gè)Failure,maybeDeferred 返回一個(gè)已經(jīng)被觸發(fā)的deferred ,并帶著failure參數(shù),或者
????假如這個(gè)函數(shù)返回了一個(gè)正常的值,maybeDeferred返回一個(gè)已經(jīng)被觸發(fā)的deferred,并帶著這個(gè)正常的值作為參數(shù),或者
????假如這個(gè)函數(shù)拋出了一個(gè)錯(cuò)誤,maybeDeferred會(huì)返回一個(gè)已經(jīng)被觸發(fā)的deferred,并帶著由這個(gè)錯(cuò)誤轉(zhuǎn)化來的failure作為參數(shù)
?
換句話說,從maybeDeferred 返回的值一定是一個(gè)deferred,即使你傳遞過去的函數(shù)不會(huì)返回deferred.這就讓我們可以安全的調(diào)用一個(gè)同步的函數(shù),并把它當(dāng)作一個(gè)返回deferred異步的函數(shù).
注意一:這里仍有一點(diǎn)不一樣,被一個(gè)同步的函數(shù)返回的deferred 是已經(jīng)被觸發(fā)過的,所以任何的你加入的callback 和errback 都會(huì)被立即調(diào)用,而不是在一些reactor loop 的迭帶之后.
注意二:也許給一個(gè)一定會(huì)返回deferred 的函數(shù)命名為maybeDeferred 不是一個(gè)特別好的選擇.
一但這個(gè)protocol 有了一個(gè)真正的deferred,它可以增加一些callback把詩送到client,并關(guān)閉相應(yīng)的連接.這個(gè)就是我們的第一個(gè)poetry proxy.
Running the Proxy
要測試我們的代理的話,先開啟一個(gè)poetry server,像下面這樣:
python twisted-server-1/fastpoetry.py --port 10001 poetry/fascination.txt
然后開啟一個(gè)proxy server:
python twisted-server-1/poetry-proxy.py --port 10000 10001
也就說proxy 運(yùn)行在10000端口,poetry server 運(yùn)行在10001端口.
下面你可以運(yùn)行一個(gè)client 連接proxy:
python twisted-client-4/get-poetry.py 10000
我們使用了一個(gè)早期的沒有poetry transformations 的client 版本.你可以看到一首詩出現(xiàn)在client 的窗口里,還有一些文字說明它正在從server 下載.如果你再運(yùn)行client 一次,這個(gè)proxy 會(huì)告訴你它正在使用緩存起來的poem.
Proxy 2.0
我們前面已經(jīng)說過,還有另外一種方法可以實(shí)現(xiàn)我們的需求.在Porxy 2.0 中有說明,代碼見twisted-server-2/poetry-proxy.py.既然我們可以在返回deferred之前觸發(fā)它,我們可以讓proxy service 在緩存中已經(jīng)存在這首詩的時(shí)候返回一個(gè)已經(jīng)觸發(fā)過的deferred.下面是proxy service 中g(shù)et_poem 的新版本:
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
這個(gè)defer.succeed 函數(shù)是創(chuàng)建一個(gè)已經(jīng)觸發(fā)的deferred并返回一個(gè)值的很便捷的方法.查看一下它的實(shí)現(xiàn)你會(huì)發(fā)現(xiàn)它就是創(chuàng)建一個(gè)deferred ,并用callback()觸發(fā) 的封裝.如果我們想返回一個(gè)已經(jīng)失敗了的deferred 我們可以用defer.fail.
在這個(gè)版本中,因?yàn)間et_poem 已經(jīng)返回了一個(gè)deferred,protocol 類不再需要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())
除了這兩個(gè)地方的變化之外,其他的沒什么變化了.你可以像上面的方法一樣來運(yùn)行它.
Summary
在這一部分我們學(xué)到了怎樣deferred 在被返回之前被調(diào)用,因而我們可以在同步的程序使用它,我們有兩種方法去實(shí)現(xiàn)它:
????我們可以使用maybeDeferred來處理時(shí)而返回deferred 時(shí)而返回正常結(jié)果的函數(shù)
????我們可以用defer.succed 和 defer.fail 提前觸發(fā)我們的deferred,所以我們的有時(shí)同步有時(shí)異步的函數(shù)可以總是返回deferred
?
我們可以使用他們中的任何一個(gè)方法.前一個(gè)強(qiáng)調(diào)了我們的函數(shù)不是總是異步的,而后一個(gè)讓代碼更簡單.沒有一個(gè)定論非要使用哪個(gè).
兩種方法都可以是因?yàn)槲覀兛梢韵騞eferred中增加callback/errback 在它被觸發(fā)之后.它也解了我們在第九部分提出的疑問.我們了解到在deferred中,不管是最后一個(gè)callback 或者errback 失敗,錯(cuò)誤會(huì)在deferred 被垃圾回收的時(shí)候才被報(bào)告出來.現(xiàn)在我們知道因?yàn)槭裁戳栓C因?yàn)槲覀兛梢砸恢毕蛞粋€(gè)deferred 對象中增加一個(gè)callback/errback 對,直到最后一個(gè)對deferred 的引用也消失了,twisted 才能認(rèn)定這個(gè)錯(cuò)誤沒有被處理.
所以,這就是deferred了嗎?我們已經(jīng)知道deferred 的全部了嗎? 對于大部分來說,是的.但是twisted 包含了很多我們還沒有探尋到的很多種交替使用deferred 的方式.同時(shí),twisted 的開發(fā)人員也在不停的增加新的特色.在將來的發(fā)布的版本中,deferred 會(huì)有更多的能力.我們會(huì)在以后的章節(jié)中講到,但首先我們需要從deferred中休息一下,看一些twisted 的其他的方面.
----
20120821 16:08
運(yùn)行 Proxy 1.0 是做為一個(gè)代理服務(wù)器,客戶端是連接到這個(gè) Proxy 1.0 上而不是外部的 server 上。如果 Proxy 1.0 中有緩存數(shù)據(jù)的話,將取到的數(shù)據(jù)通過 PretryProxyProtocol 中的 d.addCallback(self.transport.write) 發(fā)給先前的客戶端。否則向外部的 server 發(fā)起一個(gè)連接用于取數(shù)據(jù),再通過同樣的方式發(fā)送給客戶端。
總結(jié)
以上是生活随笔為你收集整理的twisted系列教程十四— pre-fireed deferred的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python获取类属性及其它(vim看源
- 下一篇: nc个人实战使用总结