[譯] 經過優化 Gunicorn 配置提升性能

關於如何配置 Gunicorn 的實用建議html

概要,對於 CPU 受限的應用應該提高集羣數量或者核心數量。但對於 I/O 受限的應用應該使用「僞線程」。前端

Gunicorn 是一個 Python 的 WSGI HTTP 服務器。它所在的位置一般是在反向代理(如 Nginx)或者 負載均衡(如 AWS ELB)和一個 web 應用(好比 Django 或者 Flask)之間。python

Gunicorn 架構

Gunicorn 實現了一個 UNIX 的預分發 web 服務端。android

好的,那這是什麼意思呢?ios

  • Gunicorn 啓動了被分發到的一個主線程,而後所以產生的子線程就是對應的 worker。
  • 主進程的做用是確保 worker 數量與設置中定義的數量相同。所以若是任何一個 worker 掛掉,主線程均可以經過分發它自身而另行啓動。
  • worker 的角色是處理 HTTP 請求。
  • 這個 in 預分發 就意味着主線程在處理 HTTP 請求以前就建立了 worker。
  • 操做系統的內核就負責處理 worker 進程之間的負載均衡。

爲了提升使用 Gunicorn 時的性能,咱們必須牢記 3 種併發方式。nginx

第一種併發方式(workers 模式,又名 UNIX 進程模式)

每一個 worker 都是一個加載 Python 應用程序的 UNIX 進程。worker 之間沒有共享內存。git

建議的 workers 數量(2*CPU)+1github

對於一個雙核(兩個CPU)機器,5 就是建議的 worker 數量。web

gunicorn --workers=5 main:app
複製代碼

Gunicorn 使用默認的 worker 模式(同步模式)。注意看這個圖片的第四行:「Using worker: sync」.

第二種併發方式(多線程)

Gunicorn 還容許每一個 worker 擁有多個線程。在這種場景下,Python 應用程序每一個 worker 都會加載一次,同一個 worker 生成的每一個線程共享相同的內存空間。編程

爲了在 Gunicorn 中使用多線程。咱們使用了 threads 模式。每一次咱們使用 threads 模式,worker 的類就會是 gthread

gunicorn --workers=5 --threads=2 main:app
複製代碼

Gunicorn 的多線程模式就是使用了 worker 的 gthread 類。請注意圖片中的第四行 「Using worker: threads」。

上一條命令等同於:

gunicorn --workers=5 --threads=2 --worker-class=gthread main:app
複製代碼

在咱們的例子裏面最大的併發請求數就是 worker * 線程,也就是10。

在使用 worker 和多線程模式時建議的最大併發數量仍然是(2*CPU)+1

所以若是咱們使用四核(4 個 CPU)機器而且咱們想使用 workers 和多線程模式,咱們可使用 3 個 worker 和 3 個線程來獲得最大爲 9 的併發請求數量。

gunicorn --workers=3 --threads=3 main:app
複製代碼

第三種併發方式(「僞線程」)

有一些 Python 庫好比(geventAsyncio)能夠在 Python 中啓用多併發。那是基於協程實現的「僞線程」。

Gunicrn 容許經過設置對應的 worker 類來使用這些異步 Python 庫。

這裏的設置適用於咱們想要在單核機器上運行的gevent

gunicorn --worker-class=gevent --worker-connections=1000 --workers=3 main:app
複製代碼

worker-connections 是對於 gevent worker 類的特殊設置。

(2*CPU)+1 仍然是建議的workers 數量。由於咱們僅有一核,咱們將會使用 3 個worker。

在這種狀況下,最大的併發請求數量是 3000。(3 個 worker * 1000 個鏈接/worker)

併發 vs. 並行

  • 併發是指同時執行 2 個或更多任務,這可能意味着其中只有一個正在處理,而其餘的處於暫停狀態。
  • 並行是指兩個或多個任務正在同時執行。

在 Python 中,線程和僞線程都是併發的一種方式,但並非並行的。可是 workers 是一系列基於併發或者並行的方式。

理論講的很不錯,但我應該怎樣在程序中使用呢?

實際案例

經過調整Gunicorn設置,咱們但願優化應用程序性能。

  1. 若是這個應用是 I/O 受限,一般能夠經過使用「僞線程」(gevent 或 asyncio)來獲得最佳性能。正如咱們瞭解到的,Gunicorn 經過設置合適的 worker 類 並將 workers數量調整到 (2*CPU)+1 來支持這種編程範式。
  2. 若是這個應用是 CPU 受限,那麼應用程序處理多少併發請求就並不重要。惟一重要的是並行請求的數量。由於 Python’s GIL,線程和「僞線程」並不能以並行模式執行。實現並行性的惟一方法是增長**workers** 的數量到建議的 (2*CPU)+1,理解到最大的並行請求數量其實就是核心數。
  3. 若是不肯定應用程序的內存佔用,使用 多線程 以及相應的 gthread worker 類 會產生更好的性能,由於應用程序會在每一個 worker 上都加載一次,而且在同一個 worker 上運行的每一個線程都會共享一些內存,但這須要一些額外的 CPU 消耗。
  4. 若是你不知道你本身應該選擇什麼就從最簡單的配置開始,就只是 workers 數量設置爲 (2*CPU)+1 而且不用考慮 多線程。從這個點開始,就是全部測試和錯誤的基準環境。若是瓶頸在內存上,就開始引入多線程。若是瓶頸在 I/O 上,就考慮使用不一樣的 Python 編程範式。若是瓶頸在 CPU 上,就考慮添加更多內核而且調整 workers 數量。

構建系統

咱們軟件開發人員一般認爲每一個性能瓶頸均可以經過優化應用程序代碼來解決,但並不是老是如此。

有時候調整 HTTP 服務器的設置,使用更多資源或經過別的編程範式從新設計應用程序都是咱們提高應用程序性能的解決方案。

在這種狀況下,構建系統意味着理解咱們應該靈活應用部署高性能應用程序的計算資源類型(進程,線程和「僞線程」)。

經過使用正確的理解,架構和實施正確的技術解決方案,咱們能夠避免陷入嘗試經過優化應用程序代碼來提升性能的陷阱。

參考

  1. Gunicorn 是從 Ruby 的 Unicorn 項目移植而來。它的設計大綱有助於澄清一些最基本的概念。Gunicorn 架構 進一步鞏固了其中一些概念。
  2. 有態度的博文報道關於 Unicorn 怎麼講一些關鍵的特性基於 Unix 表述的很是好。
  3. Stack Overflow裏有關預分發 Web 服務模型的回答。
  4. 一些更多參考來理解怎麼微調 Gunicorn。

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索