這里我們要新寫一個(gè)Twisted版的服務(wù)器。然后,再來討論一些Deferred的新功能。
在第九、十部分,我們提出了詩歌轉(zhuǎn)換引擎這個(gè)概念。由于實(shí)現(xiàn)太過簡單,因此我們用隨機(jī)選擇來模擬了可能會(huì)出現(xiàn)的轉(zhuǎn)換失敗的情景。但如果轉(zhuǎn)換引擎位于服務(wù)器端,那么當(dāng)服務(wù)器宕機(jī)就會(huì)出現(xiàn)真實(shí)的轉(zhuǎn)換失敗的情景了。
因此,在本節(jié)我們要實(shí)現(xiàn)一個(gè)詩歌樣式轉(zhuǎn)換服務(wù)器,然后在下一節(jié),我們會(huì)重寫詩歌下載客戶端使用這一服務(wù)來學(xué)習(xí)Deferred的新功能。
到目前為止,服務(wù)器端與客戶端之間的交互都是單向的。但樣式轉(zhuǎn)換服務(wù)需要兩者進(jìn)行雙向交互-客戶端將原始式樣的詩歌發(fā)送給服務(wù)器,然后服務(wù)器轉(zhuǎn)換格式并將其返回給對應(yīng)的客戶端。因此,我們需要使用或自己實(shí)現(xiàn)一個(gè)協(xié)議來實(shí)現(xiàn)這種交互。
我們設(shè)計(jì)服務(wù)器端可以提供若干種轉(zhuǎn)換服務(wù),讓客戶端來進(jìn)行選擇。因此客戶端需要向服務(wù)器端發(fā)送兩部分信息:轉(zhuǎn)換方式與詩歌原始內(nèi)容。服務(wù)器只是將轉(zhuǎn)換格式之后的詩歌發(fā)送給客戶端。這里使用到了簡單的運(yùn)程調(diào)用。
Twisted支持若干種能解決這個(gè)問題的協(xié)議:XML-RPC, Perspective Broker, AMP。
但介紹使用其中任何一種都需要大量的時(shí)間,因此我們使用自己實(shí)現(xiàn)的協(xié)議。我們約定客戶端發(fā)送內(nèi)容格式如下:
轉(zhuǎn)換方式.詩歌內(nèi)容
我們將其以netstring格式編碼,當(dāng)然服務(wù)器回發(fā)的信息也是以netstring格式編碼。由于netstring使用了length-encoding,因此客戶端能夠識別出服務(wù)器沒有將完整詩歌回發(fā)的情況。如果你嘗試一下會(huì)發(fā)現(xiàn),之前的協(xié)議無法檢測到中途中斷傳輸?shù)那闆r。
新的服務(wù)器實(shí)現(xiàn)代碼在twisted-server-1/transformedpoetry.py中。首先,我們定義了一個(gè)TransformService類:
class TransformService(object):
def cummingsify(self, poem):
return poem.lower()
這里我們僅僅實(shí)現(xiàn)了一種轉(zhuǎn)換方法(與轉(zhuǎn)換方式同名),我們可以通過添加方法支持更多轉(zhuǎn)換方式。有一個(gè)重要的地方需要注意:格式轉(zhuǎn)換服務(wù)與具體協(xié)議的實(shí)現(xiàn)是完全分離的。將協(xié)議邏輯與服務(wù)邏輯分開是Twisted編程中常見的模式。這樣做可以通過多種協(xié)議實(shí)現(xiàn)同一種服務(wù),以增加代碼的重用性。
下面看看factory的實(shí)現(xiàn)代碼:
class TransformFactory(ServerFactory):
protocol = TransformProtocol
def __init__(self, service):
self.service = service
def transform(self, xform_name, poem):
thunk = getattr(self, 'xform_%s' % (xform_name,), None)
if thunk is None: # no such transform
return None
try:
return thunk(poem)
except:
return None # transform failed
def xform_cummingsify(self, poem):
return self.service.cummingsify(poem)
factory提供了一個(gè)transform的函數(shù),protocol就是用它來處理客戶端連接發(fā)送的詩歌格式轉(zhuǎn)換請求的。
如果發(fā)現(xiàn)不存在客戶端請求的轉(zhuǎn)換方式或轉(zhuǎn)換失敗,那么返回None。和TransformService一樣,factory與具體的協(xié)議邏輯實(shí)現(xiàn)也是相互獨(dú)立的。
有一個(gè)地方需要引起注意:我們通過xfomr_前綴式方法來獲取服務(wù)方法。這種方法在Twisted中很常見,盡管前綴經(jīng)常發(fā)生變化,并且他們經(jīng)常是依賴獨(dú)立于factory的一個(gè)對象(如此處的 TransformService)。考慮到客戶端可以發(fā)送任意的transform方法名,這是一種防止客戶端蓄意使用惡性代碼來讓服務(wù)器端執(zhí)行的方法。這種方法也提供了實(shí)現(xiàn)由服務(wù)提供具體協(xié)議代理的機(jī)制。
下面是協(xié)議實(shí)現(xiàn)代碼:
class TransformProtocol(NetstringReceiver):
def stringReceived(self, request):
if '.' not in request: # bad request
self.transport.loseConnection()
return
xform_name, poem = request.split('.', 1)
self.xformRequestReceived(xform_name, poem)
def xformRequestReceived(self, xform_name, poem):
new_poem = self.factory.transform(xform_name, poem)
if new_poem is not None:
self.sendString(new_poem)
self.transport.loseConnection()
在這個(gè)協(xié)議的實(shí)現(xiàn)中,我們通過繼承NetstringReceiver來利用了Twisted對netstrings的實(shí)現(xiàn)?;惡芎玫奶幚砹司幋a與解碼功能,我們需要做的就是實(shí)現(xiàn)stringReceived方法。換句話說,stringReceived接收的參數(shù)是客戶端編碼之后的詩歌,而無需我們再去添加額外的編碼信息。而且基類同樣管理著緩沖區(qū),即當(dāng)一首詩歌完整接收完再進(jìn)行解碼。
如果一切進(jìn)展正常的話,我們會(huì)使用NetstringReceiver的 sendString方法來將格式轉(zhuǎn)換成功后的詩歌發(fā)送給客戶端。
注意我們是如何通過定義xformRequestReceived方法將收到的信息一步步推向更高的抽象層而實(shí)現(xiàn)了Twisted的模式。
我們會(huì)在下一個(gè)部分來實(shí)現(xiàn)相應(yīng)的客戶端,這里使用一個(gè)簡單的腳本來實(shí)現(xiàn)客戶端,代碼位于twisted-server-1/transform-test中。如果你運(yùn)行服務(wù)器端于11000端口:
python twisted-server-1/transformedpoetry.py --port 11000
相應(yīng)的運(yùn)行腳本為:
./twisted-server-1/transform-test 11000
那么你會(huì)看到如下輸出(經(jīng)過netstring編碼):
15:here is my poem,
在這個(gè)部分介紹了如下幾個(gè)方面內(nèi)容:
雙向通信的基本機(jī)制是很簡單的。我們使用前面服務(wù)器端與客戶端使用的相同的技術(shù)來寫與讀數(shù)據(jù),唯一不同的是我們這次兩者都使用了(讀與寫)。當(dāng)然,一個(gè)復(fù)雜的協(xié)議需要復(fù)雜的代碼來處理接收到的數(shù)據(jù)流與格式化輸出的信息。這也是為什么使用已經(jīng)存在的協(xié)議的原因。
如果你開始覺得寫簡單的協(xié)議已經(jīng)很上手了,那么最好就開始看看Twisted對不同協(xié)議的實(shí)現(xiàn)。盡管寫一些簡單的協(xié)議有助理解Twisted的編程風(fēng)格,但在一個(gè)真實(shí)的程序中,最好是復(fù)用那些已經(jīng)實(shí)現(xiàn)并證明性能良好的協(xié)議。
最后一點(diǎn)是將協(xié)議解析邏輯與服務(wù)實(shí)現(xiàn)邏輯分開,這是Twisted編程中非常重要的一個(gè)模式。我們這個(gè)服務(wù)器程序只是一個(gè)演示,你可以想象一下真實(shí)的網(wǎng)絡(luò)服務(wù)是相當(dāng)復(fù)雜的。通過將服務(wù)與協(xié)議邏輯分開,你可以通過復(fù)用已有的服務(wù)代碼來運(yùn)行于其它的協(xié)議實(shí)現(xiàn)上。
圖27展示了一個(gè)格式轉(zhuǎn)換服務(wù)器通過兩種協(xié)議提供格式轉(zhuǎn)換服務(wù)(當(dāng)然,我們的服務(wù)器只提供了一種協(xié)議):
http://wiki.jikexueyuan.com/project/twisted-intro/images/p12_server-21.png" alt="" />
雖然在圖27中使用了兩種協(xié)議,但他們也許只是幾個(gè)協(xié)議屬性不同而已,factory共享同一個(gè)服務(wù),這樣就實(shí)現(xiàn)了代碼的復(fù)用。
本部分原作參見: dave @ http://krondo.com/?p=2101
本部分翻譯內(nèi)容參見楊曉偉的博客 http://blog.sina.com.cn/s/blog_704b6af70100qa5s.html