web伺服器,撇開它對http協議封裝這些不談,我們在這裡只是關注它的工作方式。它的工作方式基本是這樣子的:來乙個連線請求,與其建立乙個連線,然後進行相應的資料傳輸,最後斷開連線。假如它是單程序單執行緒的,那麼乙個請求過來,成功建立連線之後。在這個請求結束之前,其它請求都沒有辦法再被處理了。這樣明顯不行,web伺服器本來就是用來給千萬網民訪問的嘛。
ok,我們使用多程序方式來做。那麼來乙個請求,我就spawn乙個子程序。這樣同時有多少連線請求過來。它就會生成多少個程序。這樣做的確可以解決處理多個任務的問題。但是當同時過來千萬甚至是上億級別的請求時,要一台計算機啟動千萬個程序來處理。那簡直是天方夜譚。機器的資源早就被耗盡了。
既然瓶頸在資源上,那我們使用執行緒來替代程序,在作業系統課程中我們學過執行緒是輕量級的程序,在這裡完全可以使用執行緒來替代程序。這麼改進之後,效能的確會有很大的提公升。接下來我們觀察發現,其實這些執行緒大多數都是在阻塞態。打個比喻:我們要做一百道菜,於是我們就起了一百個灶,買了一百個鍋,開始幹了。於是有的開始去買菜,有的開始洗菜,有的開始炒菜,而事實上,在每乙個時刻,有很多灶都空著。根本沒有用到。這裡面有很大的優化空間。
除了各個作業系統自己提供的基於事件程式設計的系統呼叫之外,開源大牛們也編寫了一些事件程式設計的庫。使用這些庫來程式設計,可以簡化編碼,還可以跨平台,在各個系統上都可以正常執行。頂頂大名的就是libev。注意,libev和libevent不是乙個東西,兩者都是事件程式設計的庫,但是libev在效能上要優於libevent,成替代libevent的趨勢。
在兩年前,協程似乎是乙個很高階的東西,隨後大多數語言或多或少都支援協程。我比較熟悉的有python的gevent,lua的coroutine,go的goroutine。尤其是lua和go,語言本身就支援協程。協程也被叫做輕量級執行緒。通俗點講就是定義一大堆任務,然後通過乙個執行緒輪著對每個任務都執行一下,協作執行。它的厲害之處在於每執行到乙個任務的時候,它都可以從這個任務上一次中斷的地方開始執行。在我們一般的印象中,只有作業系統對執行緒進行排程的時候才會幹這樣的事情,進行各種進棧,儲存狀態。而協程,總共也只是執行在乙個執行緒中,要是使用執行緒本身的系統棧,早就暴了。因此在這裡,實現的時候是用記憶體來模擬棧的操作。具體實現,我想複雜度一定會不小。
我們知道,執行緒比程序輕量級,因此產生乙個執行緒消耗的資源比程序少,上下文切換也比程序節約。而協程比執行緒更加輕量級,上下文切換更是迅速。於是在伺服器程式設計方面給人無限想象。儘管目前還沒有出現一款主流的採用協程的web伺服器。但是go語言開發的web服務的效能已經嶄露頭角了。
寫到這裡,似乎非同步和非阻塞的問題也就差不多了。但是在實際使用中,卻還有很多疑問。先說一下併發和並行的區別。
而在web開發上遇到的模型,絕大多數都是併發。也就是說,往往瓶頸不是在計算資源上,而是在i/o。這裡的i/o包括磁碟i/o和網路i/o。在上面,我主要討論了web伺服器在處理併發情況下的應用手段。目前採用比較普遍的是事件模型。上面事件模型處理的事情有乙個共同的特點,全都是對socket事件進行操作。而新的問題恰恰出現在這裡。這些事件庫並不支援對regular file
的非阻塞操作。因此,當我們要對檔案進行操作的時候,只能被阻塞了。而nodejs作者在實現node的時候採用了這樣的方法
uv庫作為對libeio和libev的乙個封裝,為上層提供呼叫介面,socket file
和regular file
的非阻塞操作由libev和libeio來實現。那麼libeio是怎麼實現對regular file
操作的非阻塞的呢?很簡單,執行緒池+同步模擬。當然內部實現方式仁者見仁,智者見智。我也沒有去仔細研究它的源**。
在討論非同步非阻塞的時候,大多時候忽略了對regular file
操作的非阻塞。node在語言層面上就實現了非同步非阻塞。而python只是通過事件庫等方式來實現非同步。amix大牛寫了一篇關於non-blocking servers
的部落格。其中就講到了tornado伺服器。當使用阻塞的依賴庫的時候,tornado這樣的webserver的效能將會大打折扣。
在這裡分析一下為什麼即使使用tornado的非同步效果,在使用阻塞的庫的時候還是會導致整個程序阻塞。首先要明確單例項tornado執行的時候是單執行緒的,那麼當訪問資料庫的時候阻塞10秒鐘,這個程序就會被卡住10秒鐘,卻幹不了其它事情。因為tornado的非同步核心在於ioloop這個模組。而ioloop是基於事件庫實現的,當你訪問資料庫的時候,使用的是自己的資料庫驅動程式,那麼tornado自身帶的非同步機制根本就沒有辦法發揮出來,所以會造成阻塞。因此,根據我的理解,要發揮出tornado的非阻塞功能,只有使用它自帶的非同步庫,或者使用第三方庫也是非同步的,否則根本不會有效果。
非同步非阻塞的實現有很多種方式,多程序,多執行緒,基於事件庫,使用aio庫。但是要明白事件庫和aio庫是兩個不同的技術實現。在linux下,事件庫主要用來處理socket,管道等。但是它沒有辦法處理regular file
的非同步讀寫。aio目前在linux下有glibc aio
,libeio
等庫。它們主要使用執行緒池進行模擬。而另外的kernel native aio
由於只支援o_direct
方式對磁碟讀寫,應用也甚少。
非同步和非阻塞
今天看了篇知乎討論,將非同步和非阻塞講的透徹 在這裡整理出來 同步和非同步關注的是訊息通訊機制 同步,就是在發出乙個呼叫時,在沒有得到結果之前,該呼叫就不返回。但是一旦呼叫返回,就得到返回值了。換句話說,就是由呼叫者主動等待這個呼叫的結果。非同步則相反,呼叫在發出之後,這個呼叫就直接返回了,所以沒有...
同步 非同步 阻塞和非阻塞
同步 非同步 阻塞和非阻塞 在進行windowsapi winsock 網路程式設計時,我們常常見到同步 非同步 阻塞和非阻塞四種呼叫方式。這些方式其實都可以擴充套件為廣義的概念,幫助我們理解多執行緒,多程序,實時作業系統等更廣的概念。同步 synchronic 所謂同步,就是在發出乙個功能呼叫時,...
同步 非同步 阻塞和非阻塞
在進行網路程式設計時,我們常常見到同步 非同步 阻塞和非阻塞四種呼叫方式。這些方式彼此概念並不好理解。所謂同步,就是在發出乙個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。按照這個定義,其實絕大多數函式都是同步呼叫 例如sin,isdigit等 但是一般而言,我們在說同步 非同步的時候,特指那些需...