網路伺服器效能優化
正文
Port 由 16 bit 表示,埠號0是被保留的,不可使用。1–1023 系統保留,只能由root使用者使用。1024–4999 由用戶端程式自由分配。5000–65535 由伺服器端程式自由分配。在UDP協定中,來源埠號可選擇是否填上,如果設為0,則代表無來源埠號。
在 OS 上面,可以用一個行程綁定一個 Port,負責專門處理那個 Port 的東西。
所以通常我們會用一個 Worker Process 在那個 Port 收東西,每收到一個連線,會
- 產生一個 FD 來代表此連線
- 然後產生一個 Process 來處理此 FD 連線
針對第一點,通常預設作業系統一個 Process 只能開啟最多 1024 Sockets,所以一些高效能伺服器會讓你設定系統超過 1024 以上的數字,像是 Nginx 允許你在設定檔中寫 worker_rlimit_nofile 65535。
第二點則是使用多路負用,像是 epoll,參考後面章節。
Worker Process
如同上面講的,現在有一個 Worker Process 專門可以處理流量,用 epoll 等技術,但這樣才一個 Worker Process 只能在一個 Core 上面跑。
在現代 Linux(3.9 版本以後)中,Nginx 可以使用核心提供的 SO_REUSEPORT 功能。
- 核心負載平衡:Linux 核心會在網路層級直接做「分流」
- 效率極高:當一個 SYN 封包(新連線)進來時,核心會根據雜湊(Hash)演算法,直接決定要把這個連線丟給哪一個 Worker
- 互不干擾:Worker 之間不再需要搶鎖(Lock),每個 Worker 專心處理分配給自己的連線
為了效能的極致,我們甚至會禁止 Worker 在不同核心之間「跳來跳去」(減少快取失效),在 nginx 設定檔設定 worker_cpu_affinity auto; 這會強迫 Worker 1 永遠住在 Core 1,Worker 2 永遠住在 Core 2。
多路復用
C10M
上面講的比較像是以前 C10K C100K 可以用到的技術,但針對 C1000K, C10M 考慮的就可能不是軟體優化,可能要考慮硬體幫忙,或者是繞過 Kenrel 來直接處理封包,像是 DPDK/XDP,參考以下文章:
《The C10M Problem》 (Robert Graham, 2013):它指出 Linux Kernel 處理封包的路徑太長,導致 Nginx 無法突破千萬級連線。
Cloudflare 也自己提出代替品 Pingora,How we built Pingora, the proxy that connects Cloudflare to the Internet,因為他說 Nginx 有發呆的問題,並且兩個 Process 之間不共用連線池。