協程在Web服務器中的應用(配的圖還不錯)
協程(纖程,微線程)這個概念早就有之,各家互聯網公司也都有研究,但在國內各大論壇和大會熱起來,還是今年的事。
最近參與討論開放平臺建設和架構設計過程中,有同事提到了使用協程代替線程,能夠很大幅度的提高性能。這引發了我們團隊極大的興趣和激烈的討論。
首先,說明一下什麽是協程。
協程是用戶態的線程。傳統上線程的切換是由操作系統控制的,並且,每次切換都涉及到上下文的保存切換和用戶態與內核態之間切換的過程。而協程的切換是由用戶自己控制的,並且每次切換只涉及到上下文的保存與切換(即棧的出棧和入棧的過程)。由於同時可能存在多個線程操作同一個資源的問題,使用線程在必要的情況下需要使用“等待-加鎖-釋放”這樣一套動作。而對於同一個線程中的協程同時操作同一個資源時,則沒有這樣的問題,因為同一個線程中的所有協程都是串行執行的。當然這樣也導致了一個問題:架構在同一個線程中多個協程之上的程序,是無法利用多核資源的。最後,協程還有一個好處,則是協程能夠產生更高可讀性的代碼。
在提出了協程之後,我也提供其他幾個可供比較的概念。進程,線程,回調。
進程和線程就不多說了。了解一個進程包含一個或者多個線程,一個線程包含一個或者多個協程。在Linux中,創建進程和線程耗費的資源是一致的。
這裏強調一下回調。回調函數相對於協程而言,不會造成上下文的保存和切換,因此切換速度最快,但是也造成了使用回調函數沒有上下文的問題。當需要上下文時,只能使用全局變量來保存,不符合大部分程序員的編程習慣。
然後,描述一下是因為什麽問題導致了我們發現需要協程的。
在高並發應用中,尤其是Web應用,每臺服務器都同時會有n多的連接和邏輯處理,每個連接之間沒有邏輯關聯。傳統上對於每個連接請求,都會創建一個進程或者線程來處理該請求(先進一 點的會使用池化技術),這樣如果某種類型的請求的處理線程由於各種問題導致處理耗時過長,則由於時間片全部耗費在此線程上導致問題非常嚴重,會因為一個請 求的問題導致所有請求都變慢或者不可用。
純粹的將請求種類強制劃分開來,不同線程處理不同的請求是可以解決這個問題的,但是,終歸會有部分CPU耗費在處理時間很長的請求上,另外,更換應用的時候,總會有大量配置甚至代碼級別的更新,並非完美的解決方案。
將同步處理過程轉化為異步當然也可以解決此問題。但並非所有的同步處理過程都可以異步處理。
協程是如何解決這個問題的。
《Windows核心編程 via C/C++》一書說:fiber(Windows中的協程叫做纖程fiber)只是為了使Unix的程序更加容易移植到Windows上來而提供的,建議Windows開發者不要使用fiber。我覺得這其實是不全對的,需要看場景。作者說不要使用fiber,是因為上下文的保存和切換可以由線程來解決而回調函數可以解決在同一線程中內的切換問題,一樣不需要鎖,並且速度更快。但在高並發應用中,則不一樣,面對回調需要使用編程復雜的狀態機和令人絕望的調試,fiber的上下文保存和切換則輕松的替換掉這些,並且編程實現上非常幹凈和漂亮。這是我們選擇使用協程的原因。
當然,問題並非協程就可以解決。首先異步IO與之結合是不可避免的。其次對任何一個Web服務器,後端都有非常多的Service的。一個Request,可能會涉及到多個Service。這就需要使用Furture模式,使任何一個Request只需要等待時間最長的一個Service而不用串行等待所有的Service。再次,比較復雜的是,對於協程的調度,我們需要實現一個調度器,使對協程的調用,總是能調度到請求Service完成的協程上,而沒有完成的協程,則不會被調度。
結合這些設計元素,一個使用協程的Web服務器就能高效的運作起來。
協程應用場景
http://blog.csdn.net/huyiyang2010/article/details/6042326
協程在Web服務器中的應用(配的圖還不錯)