綠色線程是如何提升伺服器並發性能的
LightIO 是去年末開始寫的一個庫,給 Ruby 提供了低廉的綠色線程,並且可以通過 monkey patch 替代原有的 native thread。這樣伺服器端可以使用大量綠色線程,用較小的消耗來獲得更好的並發性。經過一段時間的開發, LightIO 已經比較完善,並且 monkey patch 後可以成功和 Rails 、Puma 等共同使用。於是我開始考慮如何測試伺服器使用 LightIO 後的性能,畢竟能真正的帶來性能提升才有繼續開發的動力和必要。
在性能測試前要考慮下綠色線程的原理,以及為什麼可以帶來性能提升?
LightIO 通過包裝 Ruby 標準庫的 Fiber 來提供綠色線程(在 LightIO 中叫 Beam),並維護一個綠色線程的調度系統。 這個『調度系統』並不複雜,簡單來說只有三步:
- 檢查 Beam,如果有 Beam 可以執行則執行。
- Beam 執行到 blocking 操作時,使用 nonblocking 介面替代,並掛起 Beam
- 檢查完成的 nonblocking 操作,如果有則恢復掛起的 Beam。
從步驟中可以看出,綠色線程幫我們節省的是『blocking 操作』時的等待,遇到 blocking 操作時調度器會儘可能的調度更多的綠色線程(可以認為這就是並發性的提升)。從這點來看效果和 callback 非同步編程是一樣的,但綠色線程明顯更能減少程序員的心智負擔。
所謂『blocking 操作』: 主要是指 sleep
休眠線程, Thread#join
等線程同步,還有 Socket 的讀寫。
在 Web Server 中主要的 blocking 操作就是讀寫 Socket。比如一個典型的 Rails 程序,會有查詢 RDB、Redis、請求外部 API 這些 blocking 操作。
一般來說這些操作也有非同步 nonblocking 的調用介面,但假設在邏輯上客戶端需要查詢的結果,那麼伺服器必然要得到查詢的結果才能響應給客戶端,無論是使用同步介面還是非同步介面伺服器端都需要等待結果才能響應。所以一般直接使用 blocking 介面來處理並返回。
伺服器為了同時處理更多的請求,需要開啟更多的線程。因為創建線程有不小的消耗,所以一般選擇使用線程池來處理請求,這樣也會帶來問題,假如線程池中的所有的線程都被 blocking,那麼伺服器會失去接受請求的能力。換而言之線程池的大小限制了伺服器的並發能力(忽略機器和操作系統限制)。
如果能忽略線程創建時的消耗,最理想的情況是針對每個請求開啟一個線程。
而綠色線程僅僅佔用內存,Fiber(或其它語言的 coroutine…) 僅僅是個結構體,只需要佔用幾個 byte 記錄指令地址。因為沒有創建線程的昂貴消耗,可以針對每個請求啟動一個綠色線程。將伺服器並發能力發揮到最大。
考慮這幾點,我應該儘可能選擇貼近真實世界的情況來測試性能,而不是僅僅進行 Hello World 測試(這不會發揮綠色線程的優勢),於是我決定使用 Discourse 來測試,Discourse 是一個典型的 Rails 應用並自帶 benchmark 腳本。
Discourse + LightIO 的性能測試,放在下一篇博客繼續
推薦閱讀:
※搬瓦工 - $19.9年套餐補貨完成,手慢無
※用node.js做一個伺服器
※重磅-記一次驚心動魄的阿里雲伺服器被入侵過程定位
※怎麼識別web伺服器的開發埠及伺服器的版本?
※OPNFV在雙網卡物理機群上的部署(FUEL)