Nginx中Timer的實現 紅黑樹的應用

2021-10-01 20:49:20 字數 3556 閱讀 1298

在說主題之前先來說一下linux中timer的實現:在linux中,設定定時器,是通過每次系統定時器時鐘的中斷處理程式來設定相應的軟中斷位,然後通過這個中斷處理程式掃瞄系統中所有掛起的定時器,如果發現哪個定時器超時了就呼叫相應的處理函式,也就說linux定時器是通過系統中斷實現的。

在nginx中,timer是自己實現的,而且實現的方法完全不同,它是通過乙個紅黑樹去維護所有的time節點。然後在工作程序中每一迴圈都會呼叫ngx_process_events_and_timers函式,這個函式中又會呼叫處理定時器的函式ngx_event_expire_timers,這個函式每一次都會從紅黑樹中取最小的時間值,判斷是否超時,超時就執行他們的函式,直到取出的不超時為止。類似的做法還有libevent中的小根堆維護time的思想,這兩種思想是類似的,而且也能把定時器的超時時間和一些i/o事件統一到了工作程序之中。

來看看定時器的初始化ngx_event.c中:

/*初始化計時器,此處將會建立起一顆紅黑色,來維護計時器。*/    

if (ngx_event_timer_init(cycle->log) == ngx_error)

//timer的初始化

ngx_int_t

ngx_event_timer_init(ngx_log_t *log)

ngx_event_timer_mutex = ngx_mutex_init(log, 0);

if (ngx_event_timer_mutex == null)

#endif

return ngx_ok;

}

這個函式就是呼叫 ngx_rbtree_init來初始化一顆nginx自己實現的紅黑樹。

接下來就是如何去定義乙個timer事件了:

就是利用下面這個函式,將乙個定時事件的超時時間值加入到紅黑樹中即可,與之對應的還有乙個從紅黑樹中刪除的操作函式:ngx_event_del_timer

//將乙個定時時間加入到紅黑樹中,交給紅黑樹維護

static ngx_inline void

ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer) //timer說白了就是乙個int型別的值,表示超時的時間值,紅黑樹節點的key就是這個timer

ngx_del_timer(ev);

}ev->timer.key = key;

ngx_log_debug3(ngx_log_debug_event, ev->log, 0,

"event timer add: %d: %m:%m",

ngx_event_ident(ev->data), timer, ev->timer.key);

//需要進行加鎖操作保證執行緒安全

ngx_mutex_lock(ngx_event_timer_mutex);

ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer); //將定時事件的timer域插入到紅黑樹當中

ngx_mutex_unlock(ngx_event_timer_mutex);

ev->timer_set = 1;

}

那麼nginx是如何處理定時事件的

在ngx_process_events_and_timers中對定時事件處理,先去把當前紅黑樹中最小的事件找到,然後傳遞給epoll的wait,這一點保證epoll可以處理超時事件,因為如果epoll收到超時事件卻沒有收到這個那麼超時事件就不會得到處理了:

if (ngx_timer_resolution)  else 

#endif

}

尋找最小的節點ngx_event_find_timer:

//用於返回當前紅黑樹當中的超時時間,說白了就是返回紅黑樹中最左邊的元素的超時時間

ngx_msec_t

ngx_event_find_timer(void)

ngx_mutex_lock(ngx_event_timer_mutex);

root = ngx_event_timer_rbtree.root;

sentinel = ngx_event_timer_rbtree.sentinel;

node = ngx_rbtree_min(root, sentinel); //找到紅黑樹中key最小的節點

ngx_mutex_unlock(ngx_event_timer_mutex);

timer = (ngx_msec_int_t) (node->key - ngx_current_msec);//返回的是還剩下的超時時間

return (ngx_msec_t) (timer > 0 ? timer : 0);

}

在ngx_process_events_and_timers函式中獲取這個值,因為這個值是要用來設定epoll的wait時間的,保證epoll在處理的時候,最小的超時事件既然能及時得到處理,其他就都能都得到處理了,否則,已經超時了,epoll還未處理,那也就不能達到定時的目的了.

接下來:

就是說epoll wait事件是耗時的,在耗時的時間中,已經超時的就應該被刪除,同時呼叫相應的處理函式處理.

/*delta是上文對epoll wait事件的耗時統計,存在毫秒級的耗時 

就對所有事件的timer進行檢查,如果time out就從timer rbtree中,

刪除到期的timer,同時呼叫相應事件的handler函式完成處理。

*/

if (delta)

在ngx_process_events_and_timers中呼叫gx_event_expire_timers處理所有的定時事件,那麼為什麼要去判斷這個delta呢?因為這個值統計的是處理其餘事件的用時,如果用時超過了毫秒,那麼就應呼叫ngx_event_expire_timers這個函式處理所有的定時,否則就不會呼叫,因為剛處理完,完全沒必要再去處理一次,效能就是在這些細節中提公升上來的。

ngx_event_expire_timers函式:

void ngx_event_expire_timers(void)

node = ngx_rbtree_min(root, sentinel); //獲取key最小的節點

/* node->key <= ngx_current_time */

if ((ngx_msec_int_t) (node->key - ngx_current_msec) <= 0)

break;

}ngx_mutex_unlock(ngx_event_timer_mutex);

}

總結:

在nginx中timer的實現是通過這種從紅黑樹中選取最小定時事件的方式,利用了紅黑樹查詢的高效,再結合自己對效能的優化,將定時器融入到worker程序之中,在nginx的執行環境之中,這種方式可能比linux中斷那一套更為高效,真的是太有智慧型了!

實現C 中的紅黑樹

rbtree.h pragma once include using namespace std enum color template class t struct rbtreenode 紅黑樹節點 rbtreenode left rbtreenode right rbtreenode paren...

關於C 中的Timer

關於c 中timer類 在c 裡關於定時器類就有3個 1.定義在system.windows.forms裡 2.定義在system.threading.timer類裡 3.定義在system.timers.timer類裡 例 使用system.timers.timer類 system.timers....

MFC中timer 的使用

首先,要設定定時器。在init中 或其他的初始化函式裡 呼叫函式 uint settimer uint nidevent,定時器標記,uint nelapse,間隔時間 void callback lpfntimer hwnd,uint,uint,dword null 如settimer 1,200...