不服跑個分 - 頂級 Swift 服務端框架對決 Node.js
前言
最近我在做服務端 Swift 工作時,我被問到這樣的問題:
「在服務端 Swift 能否擊敗 Node.js?」
Swift 是一個可以被用來做包括服務端在內的任何事情,從他第一次開源并且移植到 Linux 上就一直很引人入勝。你們肯定有很多人像我一樣好奇,所以我非常樂意來分享我的學習成果。
最受歡迎的服務端 Swift 框架
在寫這篇文章的時候,按照 Github 上獲得 star 的數量順序排列最受歡迎的服務端 Swift 框架如下:
本文組織形式
本文將以以下方式呈現:
-
這份快速指引
-
結果摘要
-
方法學
-
詳細的結果
-
結論和說明
結果摘要
以下是主要測試的結果摘要,我想說的是:
無論各項得分怎樣,這些框架內所有的表現都非常棒
方法學筆記
為什么使用博客和 JSON?
搭博客比打印 "Hello, World!" 到屏幕上有常見的用途,JSON 也是一種很常見的用例。良好的基準測試需要考慮每個框架在相似負載下的表現,它需要比簡單的打印兩個單詞到屏幕上承載更多的壓力。
保持做相同的事情
在每一個主題測試項目中我都會盡量保證博客盡可能相似,同時貼合每個框架的語法風格來完成。為了在許多數據結構中一字不差的使用不同框架生成相同的內容,讓每個框架都使用相同的數據模型工作,但是有些方面例如 URL 路由等方式會有很大的差別來適應每個不同框架中的語法和風格。
一些微小的差別
在不同的 Swift 服務端框架直接有一些微小的差別需要注意。
-
在 Kitura 和 Zewo 中,如果絕對路徑中存在空格都會在構建時引發一些問題,在 Xcode 中構建任何框架也存在相同的問題。
-
Zewo 使用 05-09-a 的 Swift 快照版本,這意味著他在 release 模式下的構建存在一些問題,所以他運行在 debug 模式下。因為這個問題存在所以所有關于 Zewo 的測試都運行在 debug 模式下(這將不包含 release 優化)。
-
靜態文件的處理是一個眾多服務端 Swift 框架爭議的焦點。Vapor 和 Zewo 都建議使用 Nginx 來作為靜態文件的代理,然后使用框架來作為后端使用。Perfect 的建議是使用其內置的處理程序,但我并沒有有看見 IBM 對此相關的任何評論。由于這項研究不是為了探討框架如何連接 Nginx 這樣的服務器應用,所以靜態文件都使用了每個框架本身來處理。你或許可以為了性能考慮而在選擇 Vapor 和 Zewo 的時候考慮這個問題,這也是為什么我考慮包含 JSON 測試的一個原因。
-
[在 9 月 1 日更新的結果] Zewo 是一個單線程應用程序,你可以通過在每一個 CPU 上都運行一個實例來獲得額外的性能提升,因為他們是并發運行而不是在多線程模式下工作。在本研究中,每個應用程序只會有一個實例運行。
-
工具鏈 (Toolchains),每個框架都從 Apple 釋出的工具鏈中選擇了不同的快照版本,在本文發布時測試的版本如下:
-
DEVELOPMENT-SNAPSHOT-2016-08–24-a for Perfect
-
DEVELOPMENT-SNAPSHOT-2016-07–25-a for Vapor & Kitura
-
DEVELOPMENT-SNAPSHOT-2016-05–09-a for Zewo
-
Vapor 運行 release 特殊語法。如果你只是簡單的去執行二進制包,你將會在控制臺中獲得一些可以幫助開發和調試過程的日志記錄。這將會帶來一些額外的性能開銷,為了讓 Vapor 運行在 release 模式下你需要添加 --env=production 來運行,例如:
.build/release/App --env=production
-
[在 9 月 1 日更新的結果] 當使用 Zewo 的時候,即使你不能在 05-09-a 工具鏈上使用 release 模式,你依然可以通過添加以下代碼來進行 release 優化:
swift build -Xswiftc -O
-
Node.js / Express 沒有構建編譯,因為他沒有 debug 和 release 的區別。
-
靜態文件處理包括了 Vapor 的默認中間件。如果你沒有使用靜態文件并且想要優化速度(譯注:原作者的意思是如果沒有用到它來處理靜態文件,那么用這個方法來忽略掉 Vapor 默認的中間件以提高速度。),你必須包含如下代碼(就像我在 VaporJSON 中所做的一樣):
drop.middleware = []
為什么使用 Node.js / Express?
我決定使用 Node.js 的 Express 來作為一個對照包含在測試中。因為他和 Swift 服務端框架具有非常相似的語法并且被廣泛應用。他有助于建立一個基線來展示 Swift 能夠多么的讓人印象深刻。
開發博客
在某些時候開始,我稱之為「追逐彈球」。目前 Swift 服務端框架處于非常活躍的開發狀態,因為 Swift 3 的每一個預覽版相對于上一個都有成堆的改動。所以 Apple 的 Swift 團隊導致所有的服務端 Swift 框架需要頻繁的發布新版本。他們沒有擁有完善的文檔,所以我非常感謝框架的小組成員和廣大 Swift 服務端框架社區。我也要對無數的社區成員和框架團隊在我前進道路上給予的幫助表示感謝。這有很多的樂趣,我很樂意這樣做。
一個額外聲明,即使不需要許可說明,我也認為這個需要聲明一下:所有包含在源碼中的資源都來自 Pixbay 的無版權圖片,這對于制作一個示例程序很有幫助。
環境和變量
為了盡量消除不同環境帶來的影響,我使用了一個 2012 款的 Mac mini 并且重新安裝了 El Capitan (10.11.6),然后下載了 Xcode 8 beta 6,并且設置 command-line-tools 為 Xcode 8。然后使用 swiftenv 安裝了必要的快照版本,克隆倉庫并且在 release 模式下清潔的編譯每一個博客,并且不會同時進行兩個測試。測試服務器的規格是這樣的:
而在開發中我使用的是 2015 款的 rMBP。我在這里進行了構建測試,因為它是我現實生活中的開發設備所以更有意義。我用 wrk 來獲得評分,并且我使用 Thunderbolt 2 線纜來連接兩臺設備,因為 Thunderbold 橋接能擁有一個令人難以置信的帶寬使得你的路由器不會成為限制條件,他能更可靠的在博客單獨運行在一臺機器上的時候用另一個獨立的機器去生成負載以壓倒性的測試服務器。這提供了一個一致的測試環境,所以我可以說每個博客都是在相同的硬件和條件下運行,為了滿足一些好奇心,我開發設備的規格是:
測試基準
在測試中,我決定使用 4 個線程各生成 20 個連接并持續 10 分鐘。4 秒鐘不能稱之為測試,而 10 分鐘是一個合理的時間,因為能獲得大量的數據并且 4 個線程運行 20 個連接會對博客造成沉重的負擔而不至于斷開鏈接。
詳細結果
構建時間
我認為可能需要先看一眼構建時間。構建時間在日復一日的開發中占據了很大一部分開發時間,并且他也能算作是框架的性能表現,我覺得我在探索的是真實的數字和持續時間的感覺。
如何運行
對于每一個框架,
swift build --clean=dist
然后
time swift build
運行完之后,進行第二次測試
swift build --clean
最后
time swift build
這兩次構建都使用了 SPM(Swift Package Manager, Swift 包管理器) 來管理依賴關系,包括常規的、清潔的依賴都已經下載好了。
怎么運行的
這運行在我本地的 2015 款 rMBP 上并且構建在 debug 模式,因為在使用 Swift 開發應用時這是正常的過程。
構建時間結果
內存使用
我第二在意的就是在框架運行時候內存的占用量。
如何運行
第一步 開始內存占用(單純的啟動進程)
第二步 測試我服務器上峰值內存占用
wrk -d 1m -t 4 -c 10
第三步 用下面的方法第二次測試內存占用
wrk -d 1m -t 8 -c 100
怎么運行的
這個測試在一個干凈的 Mac mini 專用測試服務器上運行。反映了每個框架在 release 模式可能存在的狀況。同一時間只有一個框架在命令行中運行并且會在下一次測試前重啟。在測試期間唯一打開的窗口是活動監視器,我用它來可視化內存占用。在每個框架運行的時候,我只是簡單的指出峰值出現在活動監視器中的時候。
內存占用結果
線程使用
我第三看重的事情是每個框架在負載下的線程使用情況
如何運行
第一步 開始內存占用(單純的啟動進程)
第二部 在我的測試服務器上用下面的命令來產生線程使用:
wrk -d 1m -t 4 -c 10
怎么運行的
這是一個用干凈的 Mac mini 來搭建的專用測試服務器,每個框架都盡可能的在 release 模式下執行的。同一時間只有一個框架在命令行中運行并且會在下一次測試前重啟。在測試期間唯一打開的窗口是活動監視器,我用它來可視化內存占用。在每個框架運行的時候,我只是簡單的指出峰值出現在活動監視器中的時候。
對于這些結果的說明
這里沒有「勝出」這一類。許多不同的應用程度對于線程的管理方式不同,并且這些框架也不例外。例如 Zewo 就是一個單線程應用程序,他永遠不會使用大于一個線程(如果你沒有主動在每一個 CPU 上運行的話)。而 Perfect 則會使用每一個可用的 CPU,Vapor 則是為每個線程模型使用一個 CPU。因此該圖的目的是使線程負載峰值更容易看到。
線程使用結果
博客測試
第一個基準測試是處理 /blog 的路由,這是一個為每個請求返回 5 個隨機圖片的假博客文章接口。
如何運行
wrk -d 10m -t 4 -c 20 http://169.254.237.101:(PORT)/blog
從我的 rMBP 上用 Thunderbolt 橋接運行每個博客。
怎么運行的
在內存測試中,每個框架都在 release 模式運行,每次測試之前都會被重新啟動。同一時間只有一個框架會被運行在服務器上。所有的活動都保持在最小的改變以保證環境盡可能相似。
結果
JSON 測試
由于每個人對于靜態文件的處理方法都各有風格,所以看上去更加公平的方式是使用簡單的接口來進行相同的測試,所以我增加了 /json 路由來測試每個應用從沙盒內返回 0~1000 之間的隨機數。這個測試是單獨進行的,以保證靜態文件處理程序和中間件不會影響到接結果。
如何運行
wrk -d 10m -t 4 -c 20 http://169.254.237.101:(PORT)/json
對每個 JSON 項目都運行
怎么運行的
在其他測試中,每個框架都在 release 模式運行,每次測試之前都會被重新啟動。同一時間只有一個框架會被運行在服務器上。所有的活動都保持在最小的改變以保證環境盡可能相似。
Results
結論
我的問題得到的回答是壓倒性的 是。Swift 能做的不僅能作為服務端框架使用,并且所有的 Swift 服務端框架性能都表現得令人難以置信的好,而 Node.js 在每個測試中都排在最后兩名。
由于服務端 Swift 框架可以和其它 Swift 應用共享基本代碼庫,所以它可以為你節省大量的時間。而從這里的結果可以看出,服務端 Swift 框架在編程領域是非常強有力的競爭者。我個人會在編程中(特別是在服務端)盡可能的使用 Swift。我也迫不及待地想看到社區涌現出更多令人感到驚奇的項目。
參與其中
如果你對服務端 Swift 感興趣,現在是時候參與其中了!這些框架還有大量的工作需要完成,比如說他們的文檔。并且有一些非常炫酷的應用程序作為示例(有開源也有閉源)。你可以在這里了解更多信息:
來自:https://www.oschina.net/news/77458/top-server-side-swift-frameworks-vs-node-js