1. 程式人生 > >HAProxy 研究筆記 -- 主迴圈處理流程

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。