當前位置:成語大全網 - 書法字典 - 如何理解龍卷風

如何理解龍卷風

假設妳不知道龍卷風是什麽,也不知道為什麽要對它感興趣,那麽我就用簡短的壹句話來介紹壹下龍卷風。如果妳已經對它感興趣了,可以跳到下壹節。

Tornado是壹個用Python寫的異步HTTP服務器,也是壹個web開發框架。這個框架服務於FriendFeed網站,臉書最近已經使用了它。FriendFeed網站具有有用用戶多、應用實時性強的特點,因此其性能和可擴展性受到高度重視。現在它是開源的(多虧了臉書),我們可以徹底了解它是如何工作的。

我覺得有必要說說非阻塞IO (nonblocking IO)和異步IO (asynchronous IO AIO)。如果您已經確切知道它們是什麽,您可以跳到下壹部分。我試著用壹些例子來說明它們是什麽。

我們假設妳正在寫壹個應用,需要向其他服務器請求壹些數據(比如數據庫服務,比如新浪微博的open api),然後這些請求會花很長時間,假設需要5秒。在大多數web開發框架中,處理請求的代碼如下所示:

定義處理程序_請求(自身,請求):

answ = self . remote _ server . query(request)#這需要5秒鐘

請求.寫入_響應(answ)

如果代碼在單線程中運行,您的服務器只能每5秒鐘接收壹次客戶端請求。在這五秒鐘內,服務器做不了別的,所以妳的服務效率是每秒0.2個請求。哦,那太糟糕了。

當然,沒有人這麽天真。大多數服務器將使用多線程技術,讓服務器壹次接收來自多個客戶機的請求。假設妳有20個線程,妳的性能會提升20倍。所以現在妳的服務器效率是每秒4個請求,但這還是太低了。當然,妳可以通過不斷增加線程的數量來解決這個問題,但是線程在內存和調度上的成本是昂貴的。我懷疑妳是否使用這種改進。

如果使用AIO,很容易達到每秒數千個請求的效率。服務器請求的代碼將更改為:

定義處理程序_請求(自身,請求):

self.remote_server.query_async(請求,self.response_received)

def response_received(self,request,answ):# 5秒後調用

請求.寫入(answ)

AIO的想法是當我們等待結果時不要阻攔。而是給框架壹個回調函數作為參數,讓框架在有結果的時候通過回調函數通知我們。通過這種方式,服務器可以接受來自其他客戶端的請求。

然而,這也是AIO的缺點:代碼有點直觀。還有,如果妳使用的是Tornado這樣的單線程AIO服務器軟件,妳需要註意不要時刻阻塞任何東西,因為所有目前應該返回的請求都會像上面的處理壹樣被延遲。

關於異步IO,請參考C10K問題,比目前過於簡化的介紹更好的學習資料。

源代碼

該項目由github托管,您可以通過下面的命令獲得它,盡管通過閱讀本文您並不需要它。

git克隆git://github . com/Facebook/tornado . git

在tornado的子目錄中,每個模塊都應該有壹個. py文件,妳可以檢查它們來確定妳是否已經完全從代碼庫中移出了項目。在每個源代碼文件中,妳至少可以找到壹大段doc字符串來解釋模塊。文檔字符串中給出了壹兩個關於如何使用該模塊的示例。

IOLoop模塊

讓我們通過查看ioloop.py文件直接進入服務器的核心。該模塊是異步機制的核心。它包含壹系列打開的文件描述符(轉換器:即文件指針)和每個描述符的處理程序。它的作用是選擇那些準備讀寫的文件描述符,然後調用它們各自的處理器(IO復用的壹個實現其實就是socket的眾多IO模型中的壹個選擇模型,Java裏就是NIO)。

您可以通過調用add_handler()方法將套接字添加到IO循環中:

def add_handler(self,fd,handler,events):

" " "註冊給定的處理程序以接收fd的給定事件。"""

自我。_handlers[fd] =處理程序

自我。_impl.register(fd,events | self。錯誤)

字典類型variable _handlers保存了文件描述符(實際上是套接字)和文件描述符準備好時要調用的方法(在Tornado中,這個方法稱為處理器)之間的映射。然後,在EPOLL(UNIX中的壹種IO輪詢機制)列表中註冊文件描述符。Tornado關心三種類型的事件:讀、寫和錯誤。如您所見,默認情況下會自動添加錯誤。

自我。_impl是select.epoll()和selet.select()之壹。稍後我們將看到Tornado如何在它們之間做出選擇。

現在讓我們看看實際的主循環。不知何故,這段代碼被放在start()方法中:

定義開始(自身):

" " "啟動I/O循環。

循環將壹直運行,直到其中壹個I/O處理程序調用stop()為止

將使循環在當前事件叠代完成後停止。

"""

自我。_running = True

雖然正確:

[...]

如果不是自我。_運行:

破裂

[...]

嘗試:

event_pairs =自身。_impl.poll(poll_timeout)

除了例外,e:

if e.args == (4,"中斷的系統調用"):

logging.warning("中斷的系統調用",exc_info=1)

繼續

否則:

上升

#從待定fd集中壹次彈出壹個FD並運行

#它的處理程序。因為該處理程序可以對

#其他文件描述符,可能會有對

#這是自我更新的回路。_事件

自我。_events.update(事件對)

而自我。_事件:

fd,事件=自我。_events.popitem()

嘗試:

自我。_handlers[fd](fd,事件)

除了鍵盤中斷:

上升

除了OSError,e:

if e[0] == errno。EPIPE:

#當客戶端關閉連接時發生

及格

否則:

logging . error(" FD % d的I/O處理程序中出現異常",

fd,exc_info=True)

除了:

logging . error(" FD % d的I/O處理程序中出現異常",

fd,exc_info=True)

poll()方法以(fd: events)的形式返回壹個鍵值對,並將其賦給event_pairs變量。因為C函數庫中的poll()方法會在任何事件發生之前有信號到達時返回EINTR(實際值為4),所以需要捕捉“中斷的系統調用”這個特殊的異常。請查看man poll了解更多詳細信息。

在內部while循環中,event_pairs中的內容被逐個取出,然後調用相應的處理器。默認情況下,這裏不處理管道異常。為了使這個類適應更壹般的情況,在http處理器中處理這個異常是壹個更好的解決方案,但是現在處理它可能更容易。

註釋解釋了為什麽使用字典的popitem()方法而不是更通用的方法:

對於fd,事件在自我。_events.items():

原因很簡單。在主循環期間,this _events字典變量可以被處理器修改。例如remove_handler()處理器。該方法將fd(即文件描述符,譯者註)從_events字典中取出,所以即使選擇了fd,也不會調用它的處理器(作者的意思是如果使用for叠代loop _events,那麽_events在叠代過程中是不能修改的,否則會出現不可預測的錯誤,例如,顯然。fd,handler & gt鍵值對,但還是調用了處理程序。