python twisted教程 三–开始twisted
什么也不做,the twisted way
最后我們決定重新用twisted來實現(xiàn)我們異步的讀詩的客戶端.但首先讓我們寫一些twisted代碼找找twisted 的感覺.
下面是一個簡單到屁的twisted程序,源碼在basic-twisted/simple.py
from twisted.internet import reactor
reactor.run()
你可以像這樣運行它:
python basic-twisted/simple.py
就像我們在第二部分所說的,twisted 是一個反應器模式的實現(xiàn),包含一個反應器或者被叫做事件循環(huán)的對象,這個對象是twisted 程序的核心.在上面的程序中的第一行我們導入reactor,第二行告訴reactor去執(zhí)行一個事件循環(huán).
這個程序事實上什么也沒做,你可以用Control-C停止他,否則的話他會永遠執(zhí)行下去.一般來說我們應該給事件循環(huán)一個或者多個文件描述符讓事件循環(huán)來監(jiān)測I/O,我們后面會講到該怎么做,但現(xiàn)在反應器的事件循環(huán)是卡住的,記住反應器的事件循環(huán)不是一個不停循環(huán)的繁忙的循環(huán),如果你可以監(jiān)測cpu的使用情況,你可以發(fā)現(xiàn)這個reactor 是不耗費cpu資源的,這個反應器的事件循環(huán)會在圖片五的頂部卡住,等待將要來的事件.
這個程序仍然是一個相當簡單的程序,完全沒有吸引力是吧? 我們接下來會做點有趣的事,不過我們已經(jīng)可以得出下面的結論:
????twsited 的reactor 不會自動開始運行,你可以讓它運行起來通過reactor.run()
????反應器的循環(huán)(reactor loop)和它開始運行的地方運行在同一線程內(nèi),在這種情況下,他運行在主線程內(nèi)
????一但事件循環(huán)運行起來,就會永遠運行下去.這時reactor受程序控制
????如果沒有什么事情可做,反應器的循環(huán)不耗費CPU資源
????reactor 不用創(chuàng)建,直接import進來就可以用
最后一點需要解釋一下,在twisted中, reactor 是單例模式的.在一個twisted程序中只有一個reactor object,在你用import導入的時候就被創(chuàng)建了.如果你打開reactor 的源碼,你會發(fā)現(xiàn)只有很少的代碼,reactor 的真正實現(xiàn)在twisted.internet.selectreactor.
twisted 中有多種reactor 的實現(xiàn).在第二部分已經(jīng)提到,select 僅僅是其中的一中實現(xiàn)方法,也是twisted默認使用的方法.twisted中也包含其他的reactor 的實現(xiàn)方式,比如twisted.internet.pollreactor使用poll 的方式來實現(xiàn).
如果要使用其他的reactor,你必須要在導入twisted.internet.reactor前先安裝一下,你可以這樣來安裝pollreactor:
from twisted.internet import pollreactor
pollreactor.install()
如果你在導入twisted.internet.reactor之前沒有安裝其他的reactor 實現(xiàn),twisted默認選擇selectreactor.所以一般的不要在一個模塊的上層導入reactor用來避免不經(jīng)意的安裝了默認的reactor,相反的,要在你要引用reactor 的時候再去導入它.
Note:自從寫這些文章開始,twisted正在慢慢的轉向允許多種reactor 同時存在的結構(應該是這樣翻譯),在計劃中,一個reactor對象被傳遞的時候只作為一個引用而不是從一個模塊中導入.
Note:不是所有的操作系統(tǒng)都支持poll,如果你的系統(tǒng)不支持,上面的例子在你的系統(tǒng)跑不起來
現(xiàn)在我們可以用pollreactor重新實現(xiàn)我們的第一個 twisted 程序,你可以在basic-twisted/simple-poll.py看到源碼:
from twisted.internet import pollreactor
pollreactor.install()
from twisted.internet import reactor
reactor.run()
好吧,我們又非常成功的實現(xiàn)了一個什么也不做的poll 循環(huán).
我們在以后的教程中都會使用使用twisted 的默認的reactor,為了學習twisted的話,所有的reactor都執(zhí)行相同的功能.
Hello Twisted
下面讓我們實現(xiàn)一個做點東西的twisted程序,下面的程序會打印兩行消息:
def hello():
????print 'Hello from the reactor loop!'
????print 'Lately I feel like I\'m stuck in a rut.'
from twisted.internet import reactor
reactor.callWhenRunning(hello)
print 'Starting the reactor.'
reactor.run()
程序代碼在 basic-twisted/hello.py,如果你運行的話,會看到以下的輸出:
Starting the reactor.
Hello from the reactor loop!
Lately I feel like I'm stuck in a rut.
需要你手動殺死這個程序,因為當執(zhí)行完print 之后程序會卡住.
hello 函數(shù)是在reactor 運行之后被調用的,這就意味這hello函數(shù)是被reactor調用的,所以twisted的代碼必須可以調用我們的定義的函數(shù),我們讓callwhenrunning 傳遞一個hello函數(shù)的引用到twisted code,并觸發(fā)callwhenrunning.但讓必須在ractor運行之后才可以.
我們用術語callback來描述這種引用, 一個callback 是我們給twisted的一個函數(shù)的引用,twisted 會call back 在正確的時候.既然twisted 和我們的業(yè)務邏輯是分開的,reactor 和我們的業(yè)務邏輯通過callback一個函數(shù)來進行交互.
我們可以看到下面的程序看到twisted怎樣和我們的程序交互的:
import traceback
def stack():
????print 'The python stack:'
????traceback.print_stack()
from twisted.internet import reactor
reactor.callWhenRunning(stack)
reactor.run()
你可以在basic-twisted/stack.py看到源代碼,會輸出一些像下面的東西:
The python stack:
...
reactor.run() <-- This is where we called the reactor
...
... <-- A bunch of Twisted function calls
...
traceback.print_stack() <-- The second line in the stack function
不要在意中間的一些twisted之間的調用,注意reactor.run() 和我們的callback之間的關系
callback 是做什么的
twisted 不是唯一一個用callback 的reactor 框架, 其他的異步框架Medusa和asyncore也在用,還有一個GUI 比如GTK和QT,也是基于reactor循環(huán)的.
"反應器"系統(tǒng)的開發(fā)者們非常愛callback,但是要考慮到下面幾點:
????反應堆模式是單線程的
????一個reactor的框架比如twisted實現(xiàn)了reactor循環(huán)所以我們的代碼不要再去實現(xiàn)了
????我們的代碼仍舊需要被調用來實現(xiàn)我們的業(yè)務邏輯
????既然整個程序在單線程內(nèi),reactor循環(huán)仍需要調用我們的代碼
????reactor不能事先知道我們哪部分代碼需要被調用
考慮到上面幾點,callback就是必須的了
圖片六描述了callback 的時候發(fā)生了什么
圖片六
圖片六說明一些callback 的重要屬性:
????我們的callback 代碼和twisted 循環(huán)運行在同一個線程中
????當我們的callback在運行的時候,twisted 循環(huán)不運行
????同上,當twisted 循環(huán)運行時,callback是不運行的
????reactor循環(huán)恢復,當我們的callback返回時
?
在一個callback中,twisted 循環(huán)可以看作是被阻塞住了.所以我們應該確保我們的callback代碼不要花費很多時間.通常的,我們應該在我們的callback中避免阻塞的I/O操作.否則我們使用反應器模式的好處就不存在了.twisted 不會提供任何防范方法去阻止你的阻塞程序,我們必須確保在callback中不要做阻塞操作.不過最后你會發(fā)現(xiàn),我們不用擔心基本的網(wǎng)絡I/O操作,因為我們可以讓twisted去為我們實現(xiàn)異步通信.
其他的可能出現(xiàn)阻塞的操作包括從不是socket 文件描述符(比如pipe)中讀寫,或者等待一個子進程結束.怎樣把阻塞的操作編程的非阻塞的操作跟你要完成什么有很大的關系,但是總會有twisted api 可以幫助實現(xiàn)它.記住,一些標準的python 函數(shù)本身就是阻塞模式的,沒有方法轉為非阻塞模式,比如,os.system 函數(shù)會阻塞直到子進程運行完成,他們就是那樣工作的,所以在你用twisted的時候,你應該避免在你的代碼中使用這些阻塞函數(shù). Goodbye Twisted
你可以讓twisted 的reactor 停止運行通過 reactor 的stop方法.但是一但reactor 被停止它不能被重啟,所以在你的程序不得不停止的時候再去調用reactor 的stop方法. 曾經(jīng)在郵件列表中討論過讓reactor變?yōu)榭芍貑拥?你可以控制reactor 什么時候開始,什么時候停止.在twisted 8.2.0中,你僅僅可以啟動reactor一次
下面有一個程序,basic-twisted/countdown.py,在5秒后停止reactor:
class Countdown(object):
????counter = 5
????def count(self):
????????if self.counter == 0:
????????????reactor.stop()
????????else:
????????????print self.counter, '...'
????????????self.counter -= 1
????????????reactor.callLater(1, self.count)
from twisted.internet import reactor
reactor.callWhenRunning(Countdown().count)
print 'Start!'
reactor.run()
print 'Stop!'
這個程序用callLater API 注冊了一個callback,這個callback作為callLater 的第二參數(shù),第一個參數(shù)是等待多少秒然后你的callback才被執(zhí)行.
twisted 是怎樣在正確的實行去執(zhí)行callback 的?既然這個程序沒有監(jiān)聽任何的文件描述符,為什么也會在select循環(huán)中被卡住呢? 事實是,select 還有其他的類似的poll 也接收一個可選的timeout值,如果在一個timeout 的時間周期內(nèi)沒有任何一個文件描述符可以讀寫,select 也會返回.如果你傳遞給timeout 一個0值,你可以沒有阻塞的監(jiān)聽文件描述符.
你可以把timeout 想象成一個事件循環(huán)正在等的事件(參照圖片五),twisted 用timeout 來確保任何一個被定時了的callback可以在正確的時間運行.假如一個callback需要花費很長的時間,一個定時的callback會被順延,所以twisted 的callLater 機制不能提供非常準確的時間保證.
下面是這個程序的輸出
Start!
5 ...
4 ...
3 ...
2 ...
1 ...
Stop!
最后會輸出一個stop表示reactor 已經(jīng)退出了,現(xiàn)在有了一個可以自己的停止的程序了.
接招吧 (讓我想起了take that 樂隊)twisted
既然twisted 以callback的方法來執(zhí)行我們的方法,你會想如果一個callback拋出了異常怎么辦.讓我們試試吧,basic-twisted/exception.py會在一個callback中拋出一個異常:
def falldown():
????raise Exception('I fall down.')
def upagain():
????print 'But I get up again.'
????reactor.stop()
from twisted.internet import reactor
reactor.callWhenRunning(falldown)
reactor.callWhenRunning(upagain)
print 'Starting the reactor.'
reactor.run()
你會看到以下輸出:
Starting the reactor.
Traceback (most recent call last):
??... # I removed most of the traceback
exceptions.Exception: I fall down.
But I get up again.
注意第二個callback仍會在第一個callback之后運行,即使我們看到了很多的異常的追蹤信息.如果你把reactor.stop()注釋掉的話,這個程序會仍會繼續(xù)運行下去,所以reactor 會繼續(xù)運行下去即使我們的一個callback拋出了異常,
網(wǎng)絡服務器對魯棒性要求比較高,即使在運行過程中出現(xiàn)了一些bug也不能讓服務器down掉,不是在說我們不用去處理我們的錯誤,如果你感覺后面還有人支持著你,你會感覺很棒.
Poetry,please
下面我們要twisted去獲取點小詩啦,在第四部分,我們會實現(xiàn)一個twised 的異步的客戶端
總結
以上是生活随笔為你收集整理的python twisted教程 三–开始twisted的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python解决 Cannot unin
- 下一篇: golang错误:The process