contributed by < blueskyson
>
探討從無到有打造 Linux 平台的高效能網頁伺服器,涵蓋是否該將伺服器實作於 Linux 核心內部、並行處理、I/O 模型、epoll、Reactor pattern,和 Web 伺服器在事件驅動架構的考量。
相關資訊:
這兩年來我所習慣的開發環境為 Ubuntu20.04,但在此次作業中使用 bcc 的 eBPF 時,不管使用編譯好的工具或是編譯原始碼都遇到諸多問題,所以選擇在 Arch Linux 上開發。
隔離 cpu0 到 cpu3,這四個 cpu 只會執行 Read-copy-update,以及藉由 taskset
來把 sehttpd
固定在這四個 cpu 執行,藉此讓 sehttpd
被其他 process 搶佔的機會降到最低。
其餘 linux 系統調校還有抑制 ASLR、關閉 intel turbo mode 使得 cpu 頻率維持穩定。
最常見的方法為修改 /etc/default/grub
,使其包含以下 GRUB_CMDLINE_LINUX="isolcpus=0-3"
再執行 update-grub
來達成目的。
但是我的 arch linux 一直無法成功修改 isolcpu 這個參數,所以我用另一種方式。首先在開機時進到 grub 選單,選取將要進入的作業系統,還不要按 enter
:
按下 e
編輯開機參數:
在倒數第二行的最後面加入 isolcpus=0-3
:
按下 ctrl x
或 F10
開機,用 taskset 檢查 cpu 0 到 3 是否已經被 isolate:
epoll
與 seHTTPd 的架構epoll
是 poll
的變體,允許單一個執行緒值監看多個 file descriptor 的事件,在使用者監看的事件發生時回傳以通知使用者。事件通知的時機又分為 level trigger 和 edge trigger,在 edge trigger 模式中,只有事件觸發,或是 timeout 時 epoll_wait
才會回傳;在 level trigger 模式,epoll_wait
在事件狀態未變更前都會回傳。
sehttpd 是採用 edge trigger,在 mainloop.c 的關鍵程式碼如下:
將 seHTTPd 執行後,可以透過 ab
進行壓力測試,以下會同時開啟 1000 個連線,總共 50000 次連線連到 localhost:8081
:
只要短時間連線次數夠多,會顯示錯誤訊息:
首先 rc
為 http_parse_request_line(r)
的回傳值,為了知道 rc
確切的值,我將 http.c 第 253 行的的 log_err
改寫為:
如此一來透過 ab
測試所顯示的錯誤訊息變為:
從 http.h 中可以找到 HTTP_PARSER_INVALID_METHOD
所對應的值即為 10
:
在 http_parser.c 的第 54 行和第 92 行都會回傳此值:
read
回傳值我猜測標記 r->buf
的起始點 r->pos
指到錯誤的位置,或是超出陣列範圍(這個猜測是錯的,真正原因是下方黃底句子)。所以我將 http.c 的第 231 行的 read
下方印出 read
和 r->pos
的回傳值,檢查在什麼情況下會出問題:
此時再執行測試,錯誤訊息變為:
由上述輸出可以得知,藉由 ab
發送的 http request 大小總共為 106 byte,而上面第 10、11 行發生錯誤的情況為:r->pos
距離 MAX_BUF
(8124) 只剩 68 個 byte,所以沒辦法一次讀取 106 byte,下一次讀取 file descriptor 時,讀進來的是同一個 request 剩餘的 38 byte。然而原始程式將這 38 byte 當作下一個 request 的 head 來解析,理所當然會 parse error!
注意到 read
的回傳值 n
等於 -1
是正常現象,代表 EAGAIN
,在 non-blocking I/O 代表資料尚未準備就緒,稍後再讀取一次。
我的想法是,如果 n
等於 remain_size
,代表 buffer 讀到尾端,很有可能還有存在 fd
的資料尚未被讀取,所以要再讀取剩餘的資料,複製到 r->buf
的頭端。
在 http.c 的第 231 行後面插入以下程式碼:
對應的修改在 5217ef7。