iT邦幫忙

2024 iThome 鐵人賽

DAY 20
0
Odoo

Odoo 部署策略系列 第 20

提升效能:odoo.conf 的多行程伺服器設定介紹

  • 分享至 

  • xImage
  •  

接下來要設定我們 odoo.conf 模板中 "Performance and Limits" 的部分,不過在那之前,我們要先稍微了解多執行緒(Multi-Thread)和多行程(Multi-Process)的概念。


多執行緒(Multi-Thread)vs. 多行程(Multi-Process)

多執行緒(Multi-Thread):

在多執行緒的架構中,一個單一的程式(Process)可以同時執行多個執行緒(Thread)。這些執行緒共享同一個記憶體空間和資源,因此彼此之間的溝通和資料共享相對容易。然而,因為資源是共享的,如果沒有妥善管理,可能會導致資源競爭或死結的問題。此外,由於執行緒是輕量級的,建立和切換的成本較低,適合處理大量的小型任務。

多行程(Multi-Process):

多行程架構則是同時運行多個獨立的程式,每個行程都有自己的記憶體空間和資源。這樣的好處是可以充分利用多核心 CPU,同時執行多個任務,彼此之間互不干擾。然而,由於行程之間不共享記憶體,溝通需要透過跨行程通訊機制,可能會比較複雜,也增加了系統的複雜度和資源消耗。


CPython 中的 GIL

什麼是 GIL?

GIL(Global Interpreter Lock,全域解譯器鎖)是 Python 的 CPython 實作中的一個機制。它確保在任何時刻,只有一個執行緒在執行 Python 的 Bytecode。這個鎖的主要目的是保護 Python 解譯器中的記憶體管理,防止多個執行緒同時修改物件,導致資料不一致或系統崩潰。

GIL 對多執行緒的影響

由於 GIL 的存在,即使在多核心 CPU 上,Python 的多執行緒也無法真正同時執行 CPU 密集型的任務。執行緒需要輪流獲得 GIL,這導致無法充分利用多核心的優勢,效能提升有限。在 CPU 密集型的應用中,這成為一個明顯的瓶頸。然而,在 I/O 密集型的應用中,因為執行緒在等待 I/O 操作時會釋放 GIL,其他執行緒可以繼續執行,所以多執行緒仍然有其優勢。

如何解決 GIL 的限制

為了克服 GIL 帶來的限制,可以採用多行程(Multi-Process)模式。透過建立多個行程,每個行程都有自己的 Python 解譯器和 GIL,能夠在不同的 CPU 核心上同時執行,充分利用多核心的效能。此外,也可以使用其他沒有 GIL 限制的 Python 實作,如 Jython 或 IronPython,但這些實作可能與 CPython 不完全相容,需評估相容性問題。


odoo 的 Multi-Thread 與 Multi-Process

當我們使用預設的 odoo.conf 設定時,系統預設採用多執行緒(Multi-Thread)模式。也就是說,只有一個行程,所有的執行緒共享同一個 GIL,導致各個執行緒需要競爭 GIL,造成效能瓶頸。

官方不建議在正式部署中使用多執行緒模式,原因如下:首先,效能問題。由於 GIL 的限制,無法充分利用多核心 CPU,效能提升有限。其次,即時應用的影響。某些需要即時處理的應用(如 LiveChat)在多執行緒模式下可能無法正常運作,因為執行緒可能被阻塞。最後,穩定性考量。一個執行緒的錯誤可能影響整個行程,降低系統的穩定性。

多行程(Multi-Process)模式的優勢在於效能提升。每個行程都有自己的 GIL,能夠在多核心上同時執行,充分利用硬體資源。此外,穩定性也提高了,因為個別行程的問題不會影響其他行程,系統更為穩定。官方建議在生產環境中使用多行程模式,以獲得最佳的效能和穩定性。

需要注意的是,多行程模式僅支援 Linux,不適用於 Windows。不過,我們的部署策略透過 Docker,即使開發環境在 Windows,我們的應用實際上運行在 Linux 虛擬機中的 container 環境中,因此可以使用多行程模式。


在實驗前的官方建議計算方式

在開始實際的實驗之前,我先把官方建議的相關設定計算方式貼在下面,但因為我們是跑在 Docker 中,所以也不太準確,僅供參考,提供一個感覺的基準。

Worker 數量的計算

經驗法則:

Worker 數量 = CPU 核心數 * 2 + 1

這個公式已考量到排程任務(cron),並假設每個 worker 大約可以支援 6 個同時使用者。

記憶體大小的計算

根據系統設計的經驗,我們預估使用者請求的比例如下:

  • 20% 的請求屬於重量級任務: 大約需要 1GB 記憶體。(假設沒有設計不良的計算欄位)
  • 80% 的請求屬於輕量級任務: 大約需要 150MB 記憶體。

計算公式:

所需記憶體 = Worker 數量 * ((輕量請求比例 * 輕量記憶體需求) + (重量請求比例 * 重量記憶體需求))

設定範例

假設一台伺服器有 4 個 CPU 核心和 8 個執行緒,並有 60 名同時使用者。

  1. 計算所需的 Worker 數量:

    • 根據使用者數量:

      60 用戶 / 6 = 10(需要 10 個 worker)

    • 根據 CPU 核心數:

      (4 核心 * 2) + 1 = 9(最多可設定 9 個 worker)

    因此,我們可以設定 8 個 worker,再加 1 個用於 cron 的 worker。

  2. 計算記憶體需求:

    • 計算每個 worker 的平均記憶體需求:

      平均記憶體 = (0.8 * 150MB) + (0.2 * 1GB) = 120MB + 204.8MB ≈ 325MB

    • 總記憶體需求:

      總記憶體 = 9 個 worker * 325MB ≈ 2.93GB

    所以,我們需要大約 3GB 的記憶體。

odoo.conf 設定範例:

[options]
limit_memory_hard = 1717986918  ; 超過此限制(1.6GB)會強制終止 worker
limit_memory_soft = 858993459   ; 超過此限制(800MB)會在請求完成後回收資源
limit_request = 8192            ; 單個 worker 處理的最大請求數
limit_time_cpu = 600            ; 單個請求允許的最大 CPU 執行時間(秒)。如果請求的 CPU 執行時間超過這個限制,系統會終止該請求,以防止單一請求佔用過多的 CPU 資源。
limit_time_real = 1200          ; 單個請求允許的最大實際執行時間(秒),包括所有等待時間(如 I/O 操作)。如果請求的實際執行時間超過這個限制,系統也會終止該請求。
max_cron_threads = 1            ; cron worker 的數量
workers = 8                     ; HTTP worker 的數量

相關的介紹先到這邊,下一章我們就會實際來修改設定並進行實驗。


上一篇
探索 odoo.conf 模板:如何使用 dbfilter 與 list_db 保護資料庫
下一篇
odoo.conf 效能調整實驗:多行程與資料庫連線設定
系列文
Odoo 部署策略30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言