參考:http://www.nowamagic.net/academy/detail/13321002html
Tornado就是咱們在FriendFeed的Web服務器及其經常使用工具的開源版本。Tornado和如今的主流Web服務器框架(包括大多數Python的框架)有着明顯的區別:它是非阻塞式服務器,並且速度至關快。它還實現了一個一樣簡潔的模板框架。Tornado在本身的異步網絡訪問框架之上完整地實現了HTTP服務器,這也是它的核心框架的重要部分。python
我以爲對非阻塞IO (nonblocking IO)或異步IO (asynchronous IO, AIO)頗有必要談一談。若是你已經徹底知道他們是什麼了,能夠跳去看下一節。我儘量的使用一些例子來講明它們是什麼。ios
讓咱們假設你正在寫一個須要請求一些來自其餘服務器上的數據(好比數據庫服務,再好比新浪微博的open api)的應用程序,而後呢這些請求將花費一個比較長的時間,假設須要花費5秒鐘。大多數的web開發框架中處理請求的代碼大概長這樣:web
def handler_request(self, request): answ = self.remote_server.query(request) # this takes 5 seconds request.write_response(answ)
若是這些代碼運行在單個線程中,你的服務器只能每5秒接收一個客戶端的請求。在這5秒鐘的時間裏,服務器不能幹其餘任何事情,因此,你的服務效率是每秒0.2個請求,哦,這太糟糕了。數據庫
固然,沒人那麼天真,大部分服務器會使用多線程技術來讓服務器一次接收多個客戶端的請求,咱們假設你有20個線程,你將在性能上得到20倍的提升,因此如今你的服務器效率是每秒接受4個請求,但這仍是過低了,固然,你能夠經過不斷地提升線程的數量來解決這個問題,可是,線程在內存和調度方面的開銷是昂貴的,我懷疑若是你使用這種提升線程數量的方式將永遠不可能達到每秒100個請求的效率。設計模式
若是使用AIO,達到每秒上千個請求的效率是很是輕鬆的事情。服務器請求處理的代碼將被改爲這樣:api
def handler_request(self, request): self.remote_server.query_async(request, self.response_received) def response_received(self, request, answ): # this is called 5 seconds later request.write(answ)
AIO的思想是當咱們在等待結果的時候不阻塞,轉而咱們給框架一個回調函數做爲參數,讓框架在有結果的時候經過回調函數通知咱們。這樣,服務器就能夠被解放去接受其餘客戶端的請求了。瀏覽器
然而這也是AIO不太好的地方:代碼有點不直觀了。還有,若是你使用像Tornado這樣的單線程AIO服務器軟件,你須要時刻當心不要去阻塞什麼,由於全部本該在當前返回的請求都會像上述處理那樣被延遲返回。服務器
關於異步IO,比當前這篇過度簡單的介紹更好的學習資料請看 The C10K problem 。websocket
在深刻到模塊進行分析以前,首先來看看Tornado的設計模型。
Tornado不只僅是一個WEB框架,它還完整地實現了HTTP服務器和客戶端,在此基礎上提供WEB服務。它能夠分爲四層:最底層的EVENT層處理IO事件;TCP層實現了TCP服務器,負責數據傳輸;HTTP/HTTPS層基於HTTP協議實現了HTTP服務器和客戶端;最上層爲WEB框架,包含了處理器、模板、數據庫鏈接、認證、本地化等等WEB框架須要具有的功能。
爲了方便,約定$root指帶tornado的根目錄。總的來講,要用 Tornado 完成一個網站的構建,其實主要須要如下幾個文件:
另外可能還須要一些功能庫的支持而須要引入的文件就不列舉了,好比util和httputil之類的。來看看每一個文件的做用。
Use this whenever saving a callback to be executed later in a different execution context (either in a different thread or asynchronously in the same thread).
tornado網絡部分最核心的兩個模塊就是ioloop.py與iostream.py,咱們主要分析的就是這兩個部分。
咱們先來看看 ioloop(文檔地址:http://www.tornadoweb.org/en/stable/ioloop.html):
We use epoll (Linux) or kqueue (BSD and Mac OS X) if they are available, or else we fall back on select(). If you are implementing a system that needs to handle thousands of simultaneous connections, you should use a system that supports either epoll or kqueue.
程序中主函數一般調用 tornado.ioloop.IOLoop.instance().start() 來啓動IOLoop,可是看了一下 IOLoop 的實現,start 方法是這樣的:
def start(self): """Starts the I/O loop. The loop will run until one of the callbacks calls `stop()`, which will make the loop stop after the current event iteration completes. """ raise NotImplementedError()
也就是說 IOLoop 是個抽象的基類,具體工做是由它的子類負責的。若是是 Linux 平臺,因此應該用 Epoll,對應的類是 PollIOLoop。PollIOLoop 的 start 方法開始了事件循環。
問題來了,tornado.ioloop.IOLoop.instance() 是怎麼返回 PollIOLoop 實例的呢?剛開始有點想不明白,後來看了一下 IOLoop 的代碼就豁然開朗了。
class IOLoop(Configurable):
IOLoop 繼承自 Configurable,後者位於 tornado/util.py。
A configurable interface is an (abstract) class whose constructor acts as a factory function for one of its implementation subclasses. The implementation subclass as well as optional keyword arguments to its initializer can be set globally at runtime with configure.
Configurable 類實現了一個工廠方法,也就是設計模式中的「工廠模式」,看一下__new__函數的實現:
def __new__(cls, **kwargs): base = cls.configurable_base() args = {} if cls is base: impl = cls.configured_class() if base.__impl_kwargs: args.update(base.__impl_kwargs) else: impl = cls args.update(kwargs) instance = super(Configurable, cls).__new__(impl) # initialize vs __init__ chosen for compatiblity with AsyncHTTPClient # singleton magic. If we get rid of that we can switch to __init__ # here too. instance.initialize(**args) return instance
當建立一個Configurable類的實例的時候,其實建立的是configurable_class()返回的類的實例。
@classmethod def configured_class(cls): """Returns the currently configured class.""" base = cls.configurable_base() if cls.__impl_class is None: base.__impl_class = cls.configurable_default() return base.__impl_class
最後,就是返回的configurable_default()。此函數在IOLoop中的實現以下:
@classmethod def configurable_default(cls): if hasattr(select, "epoll"): from tornado.platform.epoll import EPollIOLoop return EPollIOLoop if hasattr(select, "kqueue"): # Python 2.6+ on BSD or Mac from tornado.platform.kqueue import KQueueIOLoop return KQueueIOLoop from tornado.platform.select import SelectIOLoop return SelectIOLoop
EPollIOLoop 是 PollIOLoop 的子類。至此,這個流程就理清楚了。
未完,待續。。。。。。。。。。。。。。。。。。。。。。