HAProxy 研究筆記 -- 主迴圈處理流程
本文簡單介紹 HAProxy 主迴圈的處理邏輯,版本為 1.5-dev17.
0. 主迴圈 run_poll_loop
HAproxy 的主迴圈在 haproxy.c 中的 run_poll_loop() 函式,程式碼如下:
/* Runs the polling loop */ void run_poll_loop() { int next; tv_update_date(0,1); while (1) { /* check if we caught some signals and process them */ signal_process_queue(); /* Check if we can expire some tasks */ wake_expired_tasks(&next); /* Process a few tasks */ process_runnable_tasks(&next); /* stop when there's nothing left to do */ if (jobs == 0) break; /* The poller will ensure it returns around */ cur_poller.poll(&cur_poller, next); fd_process_spec_events(); } }
主迴圈的結構比較清晰,就是迴圈的呼叫幾個函式,並在適當的時候結束迴圈並退出:
1. 處理訊號佇列 2. 超時任務 3. 處理可執行的任務 4. 檢測是否可以結束迴圈 5. 執行 poll 處理 fd 的 IO 事件 6. 處理可能仍有 IO 事件的 fd
1. signal_process_queue - 處理訊號隊對列
haproxy 實現了自己的訊號處理機制。接受到訊號之後,將該訊號放到訊號佇列中。在程式 執行到 signal_process_queue() 時處理所有位於訊號佇列中的訊號。
2. wake_expired_tasks - 喚醒超時任務
haproxy 的頂層處理邏輯是 task,task 上儲存著要處理的任務的全部資訊。task 的管理 是採用佇列方式,同時分為 wait queue 和 run queue。顧名思義,wait queue 是需要等 待一定時間的 task 的集合,而 run queue 則代表需要立即執行的 task 的集合。
該函式就是檢查 wait queue 中那些超時的任務,並將其放到 run queue 中。haproxy 在 執行的過程中,會因為一些情況導致需要將當前的任務通過呼叫 task_queue 等介面放到 wait queue 中。
3. process_runnable_tasks - 處理可執行的任務
處理位於 run queue 中的任務。
前面提到,wake_expired_tasks 可能將一些超時的任務放到 run queue 中。此外,haproxy 執行的過程中,還有可能通過呼叫 task_wakeup 直接講某個 task 放到 run queue 中,這代表程式希望該任務下次儘可能快的被執行。
對於 TCP 或者 HTTP 業務流量的處理,該函式最終通過呼叫 process_session 來完成,包括解析已經接收到的資料, 並執行一系列 load balance 的特性,但不負責從 socket 收發資料。
4. jobs == 0 - 無任務可執行,結束迴圈
haproxy 中用 jobs 記錄當前要處理的任務總數,一個 listener 也會被計算在內。因此, 如果 jobs 為 0 的話,通常意味著 haproxy 要退出了,因為連 listener 都要釋放了。 jobs 的數值通常在 process_session 時更新。因此,是否可以退出迴圈,就放在了所有 任務的 process_session 執行之後。
5. cur_poller.poll() - 執行 poll 處理 fd 的 IO 事件
haproxy 啟動階段,會檢測當前系統可以啟用那種非同步處理的機制,比如 select、poll、 epoll、kqueue 等,並註冊對應 poller 的 poll 方法。epoll 的相關函式介面在 ev_epoll.c 中。
這裡就是執行已經註冊的 poller 的 poll 方法,主要功能就是獲取所有活動的 fd,並 呼叫對應的 handler,完成接受新建連線、資料收發等功能。
6. 處理可能仍有 IO 事件的 fd
poller 的 poll 方法執行時,程式會將某些符合條件以便再次執行 IO 處理的的 fd 放到 fd_spec list[] 中,fd_process_spec_events() 函式會再次執行這些 fd 的 io handler。