Linux網路應用效能。 介紹

現在Web應用程式已經隨處可見,而在所有的傳輸協定中,HTTP佔據了最大的份額。 在研究 Web 應用程式開發的細微差別時,大多數人很少關注這些應用程式實際運行的作業系統。 開發(Dev)和營運(Ops)的分離只會讓情況變得更糟。 但隨著 DevOps 文化的興起,開發人員開始負責在雲端中運行他們的應用程序,因此徹底熟悉作業系統的後端對他們來說非常有用。 如果您嘗試為數千或數萬個同時連接部署系統,這尤其有用。

Web 服務中的限制與其他應用程式中的限制非常相似。 無論是負載平衡器還是資料庫伺服器,所有這些應用程式在高效能環境中都存在類似的問題。 了解這些基本限制以及如何克服它們將有助於您評估 Web 應用程式的效能和可擴展性。

我寫這一系列文章是為了回答那些想成為消息靈通的系統架構師的年輕開發人員的問題。 如果不深入了解 Linux 應用程式優化技術在作業系統層級的工作原理,就不可能清楚地理解它們。 儘管應用程式有很多種類型,但在本系列中我想探索基於 Web 的應用程序,而不是瀏覽器或文字編輯器等桌面應用程式。 本材料面向想要了解 Linux 或 Unix 程式如何運作以及如何建構它們以實現高效能的開發人員和架構師。

Linux 是 伺服器機房 作業系統,並且大多數情況下您的應用程式都在此作業系統上運行。 雖然我說的是“Linux”,但大多數時候您可以放心地假設我指的是所有類 Unix 作業系統。 但是,我尚未在其他系統上測試隨附的程式碼。 因此,如果您對 FreeBSD 或 OpenBSD 感興趣,您的結果可能會有所不同。 當我嘗試一些 Linux 特有的東西時,我會指出這一點。

雖然您可以使用這些知識從頭開始建立應用程式並且它將完美優化,但最好不要這樣做。 如果您用 C 或 C++ 為組織的業務應用程式編寫新的 Web 伺服器,這可能是您工作的最後一天。 然而,了解這些應用程式的結構將有助於選擇現有程式。 您將能夠將基於流程的系統與基於執行緒的系統以及基於事件的系統進行比較。 您將理解並理解為什麼 Nginx 的效能比 Apache httpd 更好,為什麼與基於 Django 的 Python 應用程式相比,基於 Tornado 的 Python 應用程式可以為更多使用者服務。

ZeroHTTPd:學習工具

零HTTPd 是我用 C 從頭開始寫的一個 Web 伺服器,作為教學工具。 它沒有外部依賴項,包括對 Redis 的存取。 我們運行自己的 Redis 程式。 請參閱下面的更多細節。

儘管我們可以詳細討論理論,但沒有什麼比編寫程式碼、運行程式碼並相互比較所有伺服器架構更好的了。 這是最明顯的方法。 因此,我們將使用每個模型編寫一個簡單的 ZeroHTTPd Web 伺服器:基於進程、基於執行緒和基於事件。 讓我們檢查一下每台伺服器,看看它們的效能比較如何。 ZeroHTTPd 在單一 C 檔案中實作。基於事件的伺服器包括 烏塔什,一個很棒的哈希表實現,位於單個頭文件中。 在其他情況下,沒有依賴關係,以免使專案複雜化。

程式碼中有很多註解可以幫助你理解。 ZeroHTTPd 是一個由幾行程式碼組成的簡單 Web 伺服器,也是一個用於 Web 開發的最小框架。 它的功能有限,但能夠提供靜態文件和非常簡單的“動態”頁面。 我必須說 ZeroHTTPd 對於學習如何建立高效能 Linux 應用程式很有幫助。 總的來說,大多數 Web 服務都會等待請求、檢查並處理它們。 這正是 ZeroHTTPd 要做的事。 這是一個學習工具,而不是生產工具。 它在錯誤處理方面表現不佳,並且不太可能擁有最佳安全實踐(哦,是的,我使用過 strcpy)或 C 語言的巧妙技巧。但我希望它能很好地完成它的工作。

Linux網路應用效能。 介紹
ZeroHTTPd 主頁。 它可以輸出不同的文件類型,包括圖像

留言簿申請

現代 Web 應用程式通常不限於靜態文件。 他們與各種資料庫、快取等進行複雜的互動。因此,我們將創建一個名為「留言簿」的簡單 Web 應用程序,訪客可以在其姓名下留下條目。 留言簿儲存之前留下的條目。 頁面底部還有一個訪客計數器。

Linux網路應用效能。 介紹
Web 應用程式「留言簿」ZeroHTTPd

訪客計數器和留言簿條目儲存在 Redis 中。 對於與 Redis 的通信,實作了自己的程式;它們不依賴外部函式庫。 當有公開可用且經過充分測試的解決方案時,我不太熱衷於推出自製程式碼。 但 ZeroHTTPd 的目的是研究 Linux 效能和存取外部服務,而服務 HTTP 請求會對效能產生嚴重影響。 我們必須完全控制每個伺服器架構中與 Redis 的通訊。 在某些體系結構中,我們使用阻塞調用,在其他體系結構中,我們使用基於事件的過程。 使用外部 Redis 用戶端程式庫將無法提供此控制。 此外,我們的小型 Redis 用戶端僅執行一些功能(獲取、設定和遞增鍵;獲取和附加到數組)。 另外,Redis協議極其優雅和簡單。 你甚至不需要專門教它。 該協議用大約一百行程式碼完成了所有工作,這一事實表明它是經過深思熟慮的。

下圖展示了當客戶端(瀏覽器)請求時應用程式會做什麼 /guestbookURL.

Linux網路應用效能。 介紹
留言簿應用程式的工作原理

當需要發布留言簿頁面時,需要對檔案系統進行一次呼叫以將模板讀入內存,並對 Redis 進行 XNUMX 次網路呼叫。 模板檔案包含上面螢幕截圖中頁面的大部分 HTML 內容。 內容的動態部分還有特殊的佔位符:貼文和訪客計數器。 我們從 Redis 接收它們,將它們插入頁面並為客戶端提供完整的內容。 可以避免對 Redis 的第三次調用,因為 Redis 在遞增時返回新的鍵值。 然而,對於我們的伺服器來說,它具有基於非同步事件的架構,大量的網路呼叫對於學習目的來說是一個很好的測試。 所以我們丟棄訪客數量的Redis回傳值,並透過單獨的呼叫來查詢它。

伺服器架構 ZeroHTTPd

我們正在建立具有相同功能但不同架構的 ZeroHTTPd 的七個版本:

  • 迭代
  • Fork 伺服器(每個請求一個子進程)
  • 預分叉伺服器(進程預分叉)
  • 具有執行緒的伺服器(每個請求一個執行緒)
  • 具有預先線程創建的伺服器
  • 基於架構 poll()
  • 基於架構 epoll

我們透過向伺服器載入 HTTP 請求來衡量每種架構的效能。 但在比較高度並行架構時,查詢數量會增加。 我們測試三次併計算平均值。

測試方法

Linux網路應用效能。 介紹
ZeroHTTPd 負載測試設置

運行測試時,所有組件不要在同一台機器上運行,這一點很重要。 在這種情況下,由於元件競爭 CPU,作業系統會產生額外的調度開銷。 測量每個選定伺服器架構的作業系統開銷是本次練習最重要的目標之一。 增加更多變數將對流程產生不利影響。 因此,上圖的設定效果最好。

這些伺服器分別做什麼?

  • load.unixism.net:這是我們在運作的地方 ab, Apache 基準實用程式。 它產生測試我們的伺服器架構所需的負載。
  • nginx.unixism.net:有時我們會想要執行一個伺服器程式的多個實例。 為此,具有適當設定的 Nginx 伺服器可充當來自以下位置的負載平衡器: ab 到我們的伺服器進程。
  • Zerohttpd.unixism.net:在這裡,我們在七種不同的架構上運行我們的伺服器程序,一次一個。
  • redis.unixism.net:此伺服器運行 Redis 守護進程,其中儲存留言簿條目和訪客計數器。

所有伺服器都運行在同一處理器核心上。 這個想法是評估每個架構的最大效能。 由於所有伺服器程式都在相同的硬體上進行測試,因此這是比較的基準。 我的測試設定由從 Digital Ocean 租用的虛擬伺服器組成。

我們在測量什麼?

您可以衡量不同的指標。 我們透過向伺服器載入不同並行層級的請求來評估給定配置中每種架構的效能:負載從 20 個同時使用者成長到 15 個並髮使用者。

測試結果

下圖顯示了不同架構上的伺服器在不同並行層級下的效能。 y 軸是每秒請求數,x 軸是並行連線數。

Linux網路應用效能。 介紹

Linux網路應用效能。 介紹

Linux網路應用效能。 介紹

下表是結果。

每秒請求數

排比
迭代的

預分叉
流媒體
預串流媒體
英寸
epoll

20
7
112
2100
1800
2250
1900
2050

50
7
190
2200
1700
2200
2000
2000

100
7
245
2200
1700
2200
2150
2100

200
7
330
2300
1750
2300
2200
2100

300
-
380
2200
1800
2400
2250
2150

400
-
410
2200
1750
2600
2000
2000

500
-
440
2300
1850
2700
1900
2212

600
-
460
2400
1800
2500
1700
2519

700
-
460
2400
1600
2490
1550
2607

800
-
460
2400
1600
2540
1400
2553

900
-
460
2300
1600
2472
1200
2567

1000
-
475
2300
1700
2485
1150
2439

1500
-
490
2400
1550
2620
900
2479

2000
-
350
2400
1400
2396
550
2200

2500
-
280
2100
1300
2453
490
2262

3000
-
280
1900
1250
2502
大傳播
2138

5000
-
大傳播
1600
1100
2519
-
2235

8000
-
-
1200
大傳播
2451
-
2100

10
-
-
大傳播
-
2200
-
2200

11
-
-
-
-
2200
-
2122

12
-
-
-
-
970
-
1958

13
-
-
-
-
730
-
1897

14
-
-
-
-
590
-
1466

15
-
-
-
-
532
-
1281

從圖和表中可以看出,超過 8000 個同時請求,我們只剩下兩個玩家:pre-fork 和 epoll。 隨著負載的增加,基於輪詢的伺服器的效能比串流伺服器差。 線程預先創建架構是 epoll 的有力競爭對手,證明了 Linux 核心調度大量線程的能力。

ZeroHTTPd 原始碼

ZeroHTTPd 原始碼 這裡。 每個架構都有一個單獨的目錄。

ZeroHTTPd │ ├── 01_iterative │ ├── main.c ├── 02_forking │ ├── main.c ├── 03_preforking │ ├main main.c ├── 04_preforking │ ├main main.c ├── 05 ✔ . 06_prethreading │ ├── main.c ├── 07_poll │ ├── main.c ├── XNUMX_epoll │ └── main.c ├── Makefile ├── public │ ├── .png └── 模板└── 留言簿└── index.html

除了所有架構的七個目錄之外,頂層目錄中還有兩個:public 和 templates。 第一個包含 index.html 檔案和第一個螢幕截圖中的圖像。 您可以將其他檔案和資料夾放在那裡,ZeroHTTPd 應該可以毫無問題地提供這些靜態檔案。 如果瀏覽器中的路徑與公用資料夾中的路徑匹配,則 ZeroHTTPd 會在此目錄中尋找 index.html 檔案。 留言簿的內容是動態產生的。 它只有一個主頁,其內容基於文件“templates/guestbook/index.html”。 ZeroHTTPd 可以輕鬆新增動態頁面進行擴充。 這個想法是用戶可以將模板添加到此目錄並根據需要擴展 ZeroHTTPd。

要建立所有七個伺服器,請運行 make all 來自頂級目錄 - 所有建置都將出現在該目錄中。 可執行檔在啟動它們的目錄中尋找 public 和 templates 目錄。

Linux API

您無需精通 Linux API 即可理解本系列文章中的資訊。 不過,我建議您閱讀更多有關此主題的內容;網路上有很多參考資源。 儘管我們將涉及 Linux API 的幾個類別,但我們的重點將主要集中在進程、執行緒、事件和網路堆疊上。 除了有關 Linux API 的書籍和文章之外,我還建議閱讀 mana 以了解所使用的系統呼叫和函式庫函數。

性能和可擴展性

關於性能和可擴展性的一點說明。 理論上,它們之間沒有關聯。 您可以擁有一個運作良好的 Web 服務,回應時間為幾毫秒,但它根本無法擴充。 同樣,可能有一個效能不佳的 Web 應用程式需要幾秒鐘的時間來回應,但它可以擴展數十秒以處理數以萬計的並髮用戶。 然而,高效能和可擴展性的結合是一個非常強大的組合。 高效能應用程式通常會節省資源,從而有效地為伺服器上的更多並髮用戶提供服務,從而降低成本。

CPU 和 I/O 任務

最後,在計算中總是有兩種可能的任務類型:I/O 和 CPU。 透過 Internet 接收請求(網路 I/O)、提供檔案服務(網路和磁碟 I/O)、與資料庫通訊(網路和磁碟 I/O)都是 I/O 活動。 某些資料庫查詢可能會佔用一點 CPU 資源(排序、對一百萬個結果求平均值等)。 大多數 Web 應用程式都受到最大可能 I/O 的限制,並且處理器很少滿載使用。 當您看到某些 I/O 任務使用大量 CPU 時,這很可能是應用程式架構不佳的跡象。 這可能意味著 CPU 資源浪費在進程管理和上下文切換上——而且這並不完全有用。 如果您正在進行影像處理、音訊檔案轉換或機器學習等操作,那麼該應用程式需要強大的 CPU 資源。 但對於大多數應用程式來說,情況並非如此。

了解有關伺服器架構的更多信息

  1. 第一部分:迭代架構
  2. 第二部分。 分叉伺服器
  3. 第三部分。 預分叉伺服器
  4. 第四部分。 具有執行緒的伺服器
  5. 第五部分. 預線程伺服器
  6. 第六部分。 基於 Pol 的架構
  7. 第七部分。 基於epoll的架構

來源: www.habr.com

添加評論