(最近忙,發點舊文湊數-_-|||,這是三月份試用Twisted時記的一點筆記)
Twisted是一個功能極為強大的非同步網路應用開發庫——當然是Python的。印象中大概也就只有ACE有這麼強悍,但Twisted在易用性方面要好很多——這其中當然少不了Python的功勞(ACE是用C++的)。
但Python也有其缺點,其中之一就是因為GIL的存在使得用Python寫多線程應用的意義不大,那麼對於像Twisted這樣基於Reactor模式實現的單一Event Loop的應用開發環境來說,就有一點局限:整個應用(或者說整個進程)都必須圍繞著reactor來。這樣的話做一些純後端的應用是沒什麼問題,比如像網頁爬蟲(Scrapy就是基於Twsited的)一類的應用。但這顯然不夠,很多應用還是需要有前端介面的——比如像BT、電驢這樣的下載工具。MLDonkey採用的方法就是提供一個WEB的管理介面來與使用者互動——當然,MLDonkey並不是用Python開發的。
這是一個很好的思路。所以Twisted提供了一套WEB開發環境以實現這樣的目的。
關於twisted.web部分的開發,官方的文檔
顯得太過於簡單了,特別是連基本的request對象的參考文檔都沒有。還好gashero整理了一部分在《Twisted的WEB開發
》,引用一下(有補充):
channel :包含上級的HTTP協議對象。
transport :通訊對象。
method :HTTP方法,如GET和POST。
uri :全部請求的URI。
path :具體的請求路徑,不含參數。
args :請求參數,包括URL參數和POST參數。格式如 {’key’:[’val1′,’val2′],} 。
received_headers :請求報文的頭欄位。
received_cookies :請求報文的cookie。
content :請求報文的實體主體,檔案對象。
clientproto :發出請求的用戶端的HTTP版本。
client :請求的用戶端地址對象,包括type、host和port屬性,分別記錄協議(這裡固定為TCP)、IP和連接埠號碼
host :本機接收請求的地址對象
getHeader(key) :擷取請求的頭欄位。
getCookie(key) :擷取請求的cookie。
getAllHeaders() :所有請求的頭欄位字典,就是返回received_headers。
getRequestHostname() :請求的host欄位,不含連接埠號碼。
getHost() :原始請求的通訊地址,返回host。
getClientIP() :擷取用戶端IP。
getUser() :擷取basic驗證中的使用者名稱。
getPassword() :擷取basic驗證中的密碼。
getClient() :擷取用戶端地址對象
一個最基本的Web應用如下:
from twisted.web import server, resource<br />from twisted.internet import reactor<br />class Simple(resource.Resource):<br /> isLeaf=True<br /> def render_GET(self, request):<br /> return "Hello, world!"<br />reactor.listenTCP(8080, server.Site(Simple()))<br />reactor.run()
然後運行起來就可以通過URL:http://localhost:8080 訪問這個程式的頁面了。
注意其中的的isLeaf是必須的。不過也還有另一種方法可以實現同樣的功能:
from twisted.web import server, resource<br />from twisted.internet import reactor<br />class Simple(resource.Resource):<br /> def __init__(self):<br /> resource.Resource.__init__(self)<br /> self.putChild("", self)<br /> def render_GET(self, request):<br /> return "Hello, world!"<br />reactor.listenTCP(8080, server.Site(Simple()))<br />reactor.run()
這個方法好理解,就是把這個對象掛到請求連結的根目錄上。以此類推就可以實現帶子目錄請求的頁面:
from twisted.web import server, resource<br />from twisted.internet import reactor<br />class ChildSimple(resource.Resource):<br /> isLeaf=True<br /> def render_GET(self, request):<br /> return "Hello, child!"<br />class Simple(resource.Resource):<br /> def __init__(self):<br /> resource.Resource.__init__(self)<br /> self.putChild("", self)<br /> self.putChild("child", ChildSimple())<br /> def render_GET(self, request):<br /> return "Hello, world!"<br />reactor.listenTCP(8080, server.Site(Simple()))<br />reactor.run()
現在可以通過URL:http://localhost:8080/child 訪問這個子頁面了。不過產生子頁面還有一個動態辦法:
from twisted.web import server, resource<br />from twisted.internet import reactor<br />class ChildSimple(resource.Resource):<br /> isLeaf=True<br /> def __init__(self, id):<br /> resource.Resource.__init__(self)<br /> self.id=id<br /> def render_GET(self, request):<br /> return "Hello, No. %s visitor!" % self.id<br />class Simple(resource.Resource):<br /> def __init__(self):<br /> resource.Resource.__init__(self)<br /> self.putChild("", self)<br /> def render_GET(self, request):<br /> return "Hello, world!"<br /> def getChild(self, path, request):<br /> return ChildSimple(path)<br />reactor.listenTCP(8080, server.Site(Simple()))<br />reactor.run()
現在可以試試URL:http://localhost:8080/1234 訪問了。其中1234可以換成任何數字(實際上上面的程式對此未作限制,即使輸入任何字元也是可以的)。
接下來是靜態內容的使用:
from twisted.web import server, resource, static<br />from twisted.internet import reactor<br />class Simple(resource.Resource):<br /> def __init__(self):<br /> resource.Resource.__init__(self)<br /> self.putChild("", self)<br /> self.putChild("static", static.File("/var/www/htdocs"))<br /> def render_GET(self, request):<br /> return "Hello, world!"<br />reactor.listenTCP(8080, server.Site(Simple()))<br />reactor.run()
訪問一下URL:http://localhost:8080/static 試試就知道了。
這些對於Web開發都是些基本的功能,稍微複雜一點的網頁要做起來還是很痛苦的。但是Python是很靈活的東西,資源也很豐富,所以實際上問題也不大。比如在getChild方法裡加上routing的實現,再加一個mako一類的模板,功能立即強大很多(貌似搞得像Pylons了)——只是資料庫操作還是需要謹慎一些,簡單地加上SQLAlchemy之類的ORM也不是不行,但是它對資料庫的訪問是阻塞式的,可能導致Twisted應用的效能下降很大。
所幸的是一般來說為Twisted應用增加Web互動介面很少還有需要操作資料庫的,即使必須要有,Twisted也提供了非同步資料庫提供者——雖然功能不可能像SQLAlchemy那麼強大,對於簡單的應用應該也足夠了。
雖然WEB介面已經很方便了,但也許還會有人想要用互動式GUI,這也沒問題。Twisted的Web庫提供了對XMLRPC和SOAP的支援。以簡單一些的XMLRPC為例:
from twisted.web import server, resource, xmlrpc<br />from twisted.internet import reactor<br />class XREcho(xmlrpc.XMLRPC):<br /> def xmlrpc_echo(self, bar):<br /> return bar<br />reactor.listenTCP(8080, server.Site(XREcho()))<br />reactor.run()
用戶端的調用方式是:
import xmlrpclib<br />s = xmlrpclib.Server("http://localhost:8080/")<br />s.echo("Hello!")
上面的代碼執行後將回顯。
實際上XMLRPC對象也是一種Resource,所以也可以簡單地用putChild放到子目錄下去。或者如果需要對xmlrpc的request進行控制——比如增加authentication或者其它的用戶端調用限制,可以在_cbRender方法裡實現,具體可參考web.xmlrpc的源碼。
關於用Twisted進行Web開發的更多內容請參考官方文檔和源碼。