玩蛇記-使用tornado構建高性能Web應用

From: http://www.cnblogs.com/Alexander-Lee/archive/2010/03/20/1690292.html


這個新系列是爲了記錄在python下的工作點滴,最近從微軟平臺突然轉換到了linux平臺下工作,於是.NET不怎麼排得上用場,且在python下工作多日才發現原來在.NET下的日子真是往事不堪回首月明中……當然僅僅是一家之言,純屬個人感受。總之呢,玩蛇記這個系列就是用來專門記錄python下工作的感想之用了。

在.NET下工作崇尚集成化,集成化的IDE,一體化的服務器,從操作系統到webserver,都是ms inside。非常適合初學者,啥都不用想,也沒有太多讓你選的。慢慢的能力提高了,就會覺得微軟提供的東西不是那麼完美,當你在開發的時候,很多東西如果不用微軟內置的機制,就會讓你費老大的力氣去繞彎實現,比如DataGrid,很強大,但是大多數人都會在自己的blog離告誡後來者,還是回去用repeater吧,所見即所得的編輯器裏面也因爲各種爲了妥協而加入的代碼變得面目全非,直到後來的MVC的出現才勉強解決,不過以前在webform裏積累的很多東西和技巧就付諸東流了。

在正式開篇前說了很多對.NET的不滿,呵呵,小小偏題,幸虧博客園不是起點要按字數收錢。

話說回來,python很適合已經熟練掌握一種語言和平臺的開發人員做拓展,比如已經熟練掌握java平臺或者.NET的開發人員,當然更適合C和C++的開發人員來偶爾玩票一下Web開發。python好玩但並不是說它就是個漂亮的玩具,而是一種在linux,unix平臺下非常具備生產力的語言。在很早的時候linux和bsd系統就默認內置python了,而現在很smart的ubuntu之類的很多集成的工具都是python編寫的,而在web開發方面,估計是因爲圈子小,而很多業餘玩python的人大多不是正職搞Web開發的,所以國內用python開發Web的人不多。不過最近django之類的框架也出了書,雖然是沾了ROR的光,不過好歹也有人關注了。

其實python下最大的問題不是選擇太少,恰恰是選擇太多了,由於python的生產力實在是太強,要造個輪子的成本實在是很低,所以到處都是輪子,從Quixote,Django,web2py,web.py,karrigell……國內的還有uliweb……

絕對讓你挑得眼花繚亂,也許是每個應用的需求各不相同,所以各個框架的出發點也各有分別,對初學者來說是眼花繚亂了,但是相對來說可選的範圍也廣了。還有一點和微軟平臺的區別就是,部署上也不是一體式的,IIS上架着.NET,這點其實是以前我非常眼熱的。看着nginx,lighttpd這些新生代web server的讓人心動的benchmark,再對比看着iis(那些標榜自己高performance的web server 在做對比測試的時候都不和iis一起比,不帶一起玩的,不是一個等級……)

說到performance,我感覺自己有強烈的performance強迫症,看着跑得快的就眼熱,所以就有了今天的豬腳-tornado,

這是一個facebook收購的一家叫friend feed的公司出品的框架,其實也是一款高性能的web server,純python編寫,開源,意味着我可以自己對其進行改進,且還有一個好處就是在文檔異常缺乏的時候可以直接翻源碼解決問題,事實證明這點非常有用,tornado的文檔非常的缺乏,而且也沒有api的說明,官網上的文檔基本上等於就是把代碼裏的註釋集中到了一起,不過還好python是如此的可愛,很多時候是靠着dir(object)來解決了問題。

由於facebook被和諧掉了,所以tornado的官網也未能倖免,其實python的下載頁也被和諧掉了,不知道怎麼回事,莫非是python.com造的孽?

祭上puff,直奔www.tornado.org,下載下來,解壓,安裝:

python setup.py install 

注意,必須在linux下安裝,windows下沒有epoll,所以用select代替了,非常的沒效率,這點上twisted也是一般的光景。

由於tornado本身就是一個web server,所以自己就能單獨運行。在用了2個星期後我感覺其實不安裝,直接引用源碼運行更好一些,很多時候可以方便自己對這個系統做點小改造。

安裝後執行python,打開python shell,在其中輸入 import tornado

回車後不報錯就說明安裝成功了

現在我們就可以用tornado來編寫網站了,新建一個server.py

內容如下:

import tornado.httpserver
import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

application = tornado.web.Application([
    (r"/", MainHandler),
])

if __name__ == "__main__":
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

 

保存後,執行 python server.py

這個時候就能夠通過瀏覽器看到 hello world的頁面了

tornado和Django一樣通過正則表達式的url mapping,不過有區別的是沒有強制區分view和model,因爲tornado雖然有一個簡單的數據庫封裝,但是沒有orm,幸好我對orm也不怎麼感冒,且在python下orm確實也很雞肋,而且真要用orm也能直接用現成的,真要自己寫一個也很容易。模版系統也比較方便,不過沒有集成Session,但是內部實現了一個安全cookie。不過現在很多系統都沒用inproc的session了,所以就自己DIY了一個用memcached作爲backend的Session出來。

這些東西我會在接下來的一系列POST裏涉及到,因爲沒有寫教程的打算,所以很多細節可能就點到即止了,更詳細的相信願意用這個玩意兒的也有這個能力去啃啃源代碼,也就幾千行。

另,tornado是一個單進程,單線程的Server,所以在每個RequestHandler的方法中的代碼都是線程安全的。

很多人會有疑問,單進程單線程能快得快嗎?其實事實勝於雄辯:

image

不過也由於這個特徵,不能寫一些延遲很大的操作,現在tornado暫時只支持用異步的curl,數據庫操作暫時還是阻塞的,我正在研究增加一個異步的數據庫操作模塊來進一步提升其性能,如果有任何成果我會第一時間公佈,也希望喜歡玩新鮮的蛇友同我交流

在上面的部分,我們創建了一個helloword的應用,並且能夠通過瀏覽器訪問它,這樣我們就可以用Tornado來開發網站了,嗯,這是可喜的第一步,不過當你決定了用tornado開發網站,並且開始寫新的handler的時候,你會發現需要頻繁的,關掉,再重新啓動服務器,這是一件非常讓人抓狂的事情,我們需要在程序修改後,服務能夠自動reload新的代碼,和asp.net一樣。但是tornado的文檔實在是有限,在文檔上找不到相關的任何說明。

但是山窮水盡疑無路柳暗花明又一村,無意在源代碼裏發現了一個autoreload.py的文件

image

猜想其實tornado是可以自己reload的,不過文檔裏沒有說明,但是autoreload要怎麼用呢。打開這個文件,看到其實裏面很簡單,只有兩個函數,如下圖

image

第二個函數是私有的,所以實際上start就是唯一的入口,我們在代碼中發現,_reload_on_update函數其實在start中被作爲一個回調函數註冊到了io_loop中了,所以實際上我們只需要把ioloop傳入start就可以實現autoreload了。所以將

def main():
    tornado.options.parse_command_line()
    application = tornado.web.Application([
        (r"/", MainHandler),
    ])
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

 

這些代碼改爲:

def main():
    tornado.options.parse_command_line()
    application = tornado.web.Application([
        (r"/", MainHandler),
    ])
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(options.port)
    loop=tornado.ioloop.IOLoop.instance()
    tornado.autoreload.start(loop)
    loop.start()

這樣子就行了。

注意,這裏的reload只檢測py文件的變動,如果是其他文件發生變動,比如css,圖片這些,都不會引發reload。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章