[Nginx] timer event

Source: Internet
Author: User
Tags event timer

Transferred from: Yanyu Jiangnan

Nginx event management mainly involves network events and timer events. The following describes the timer event management, namely timeout management.

Why timeout management?

It is necessary for nginx to manage events that may have timed out in a unified manner and handle such events when the event times out, such as recycling resources and returning errors. For example, after a client sends a request connection to nginx, nginx will accept () and create a connection object to read the request header information. However, reading this header information must be completed within a certain period of time. If you have not read the header information within a limited period of time or the information in the read header is incomplete, nginx cannot process it normally and considers it an error/illegal request, directly return error information and release corresponding resources. If nginx does not do this, it is easy to implement such malicious attacks.

How to manage timeout?

For timeout management, two problems must be solved:

(1) Organization of timeout event objects
Nginx uses a red/black tree. (2) There are two methods for timeout detection of timeout event objects: one is the timing detection mechanism, by setting the timer, before a certain period of time, we performed a super scan on all timeout events managed by the red/black tree. The other is to first calculate the shortest time-out from the current time. Then wait for this time and then perform a timeout detection. Organization of timeout event objects

Nginx sets two global variables so that the red tree (src/event/ngx_event_timer.c) can be visited wherever the program is located ):

 

Ngx_thread_volatile ngx_rbtree_t ngx_event_timer_rbtree; // The Red-black tree structure for timeout management static ngx_rbtree_node_t ngx_event_timer_sentinel; // The sentinel node in the red-black tree

 

The initialization function ngx_event_timer_init () of the Red-black tree is called in the function ngx_event_process_init. That is, each worker process will create a red/black tree during its own initialization. Source code (src/eventngx_event.c ):
static ngx_int_tngx_event_process_init(ngx_cycle_t *cycle){    ....    if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {        return NGX_ERROR;    }    ...}

The ngx_event_timer_init function initializes the red/black tree structure:

/** The Event timer rbtree may contain in the duplicate keys, however, * It shoshould not be a problem, because we use the rbtree to find * a minimum timer value only */second (ngx_log_t * log) {// The Red/black tree initializes ngx_rbtree_init (& ngx_event_timer_rbtree, & signature, identifier ); // multithreading # If (ngx_threads) if (ngx_event_timer_mutex) {ngx_event_timer_mutex-> log = log; return ngx_ OK;} ngx_event_timer_mutex = ngx_mutex_init (log, 0 ); if (ngx_event_timer_mutex = NULL) {return ngx_error;} # endif return ngx_ OK ;}

While ngx_rbtree_init (tree, S, I) is a macro definition (src/CORE/ngx_rbtree.h), which creates an empty red/black tree:

 

#define ngx_rbtree_init(tree, s, i)                                               ngx_rbtree_sentinel_init(s);                                                  (tree)->root = s;                                                             (tree)->sentinel = s;                                                         (tree)->insert = i
Event timeout monitoring:

When an event needs to be monitored for timeout, it is added to the red/black tree. For example, after nginx calls accept to receive a request from the client and establishes a connection object, the following code can be found in the initialization function ngx_http_init_connection () of the connection object:

voidngx_http_init_connection(ngx_connection_t *c){...(358L)ngx_add_timer(rev, c->listening->post_accept_timeout);...}

The first parameter of ngx_add_timer is the event object, and the second parameter is the time-out period.

Timeout Detection: The specific timeout detection scheme used by nginx depends on the configuration item timer_resolution, for example, timer_resolution 100 ms. In nginx code, the value of the global variable ngx_timer_resolution is 100; let's take a look at the core processing function ngx_process_events_and_timers (ngx_event.c) of the worker process ):
Cycle (ngx_cycle_t * cycle) {ngx_uint_t flags; ngx_msec_t timer, Delta; If (timer) {timer = ngx_timer_infinite; flags = 0;} else {timer = timer (); // set the timeout detection time to the difference between the timeout time of the event object with the fastest timeout and the current time. Flags = ngx_update_time ;... (void) ngx_process_events (cycle, timer, flags );...}
As you can see, whether ngx_timer_resolution is 0 affects two values: timer and flags.
When ngx_timer_resolution is not 0, solution 1. Timer is infinitely large. The maximum time for timer to be blocked as an event mechanism in the ngx_process_events () function. So when timer is an unlimited conference, it will not cause the event processing mechanism to wait infinitely while timeout events cannot be handled in a timely manner? No. Under normal circumstances, the event processing mechanism will monitor the occurrence of some I/O events. Even if the server is too idle and no I/O event occurs, the working process will not wait infinitely. Because a timer has been set for the Worker Process at the beginning, which is implemented in the initialization function ngx_event_process_init (), check the Code:
static ngx_int_tngx_event_process_init(ngx_cycle_t *cycle){        ...        sa.sa_handler = ngx_timer_signal_handler;        sigemptyset(&sa.sa_mask);        itv.it_interval.tv_sec = ngx_timer_resolution / 1000;        itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;        itv.it_value.tv_sec = ngx_timer_resolution / 1000;        itv.it_value.tv_usec = (ngx_timer_resolution % 1000 ) * 1000;        if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,                          "setitimer() failed");        }        ....}

Callback Function ngx_timer_signal_handler:

 

static voidngx_timer_signal_handler(int signo){    ngx_event_timer_alarm = 1;#if 1    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, "timer signal");#endif}

 

We can see that it only sets the flag variable ngx_event_timer_alarm to 1.

The worker process updates the time only when ngx_event_timer_alarm is set to 1. Code in the function ngx_epoll_process_events (ngx_epoll_module.c ):
static ngx_int_tngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags){...    events = epoll_wait(ep, event_list, (int) nevents, timer);    err = (events == -1) ? ngx_errno : 0;    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {        ngx_time_update();    }...}

In case of solution 1, | if the previous formula is false, then if ngx_event_timer_alarm is not 1, the update function ngx_time_update () will not be executed. Therefore, the time-out detection function ngx_event_expire_timers will not be executed. Check the code of the ngx_process_events_and_timers function (ngx_event.c ):

Voidngx_process_events_and_timers (ngx_cycle_t * cycle ){... delta = ngx_current_msec; (void) ngx_process_events (cycle, timer, flags); // event processing function delta = ngx_current_msec-delta ;... if (DELTA) {ngx_event_expire_timers (); // timeout detection function }...}

When ngx_timer_resolution is 0, solution 2 is executed. Timer is set to the time difference between the timeout time of the event object with the fastest timeout and the current time. The actual calculation is in the ngx_event_find_timer function (ngx_event_timer.c ).

ngx_msec_tngx_event_find_timer(void){    ngx_msec_int_t      timer;    ngx_rbtree_node_t  *node, *root, *sentinel;    if (ngx_event_timer_rbtree.root == &ngx_event_timer_sentinel) {        return NGX_TIMER_INFINITE;    }    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);    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);}

This function finds the node with the smallest key value from the red/black tree, and then subtract the current time from the key value to get the expected timer value. This value may be a negative number, indicating that an event has timed out. Therefore, if the value is set to 0, the event processing mechanism will return immediately when it starts monitoring I/O events so that these timeout events can be processed immediately. At the same time, flags is set to ngx_update_time. From the code of the ngx_epoll_process_events function, we can see that ngx_time_update () will be executed and the event will be updated. That is, the event processing mechanism will update the time each time it returns. If there are many I/O events, the gettimeofday () system function will be called more frequently, which can be said to be the biggest impact of timeout detection solution 2 on performance. In this case, the time-out detection function ngx_event_expire_timers () will be executed.


Next let's take a look at the timeout detection function ngx_event_expire_timers. The main task is to check whether the timeout event object has timed out and process the timeout event object. To detect whether an event object times out, you do not need to traverse all the time-out objects for scanning. Instead, you can find the most recent time-out event object. Determine whether it times out. If it times out, remove it from the red/black tree, set the timeout tag, and call the callback function for processing. Then, judge the second nearest timeout event object, which is about to time out. If it is repeated, it is known that a timeout event object has not timed out or all timeout event objects have timed out and the detection is completed after processing.

The following is the core code:

 

Callback (void) {ngx_event_t * EV; ngx_rbtree_node_t * node, * root, * Sentinel; sentinel = iterator; // loop detection for (;) {ngx_mutex_lock (ngx_event_timer_mutex ); root = ngx_event_timer_rbtree.root; If (root = Sentinel) {return;} // find the latest timeout event object node = ngx_rbtree_min (root, Sentinel ); /* node-> key <= ngx_current_time * // If timeout has occurred if (ngx_msec_int_t) (node-> key-ngx_current_msec) <= 0) {EV = (ngx_event_t *) (char *) node-offsetof (ngx_event_t, timer); ngx_log_debug2 (ngx_log_debug_event, ev-> log, 0, "Event timer DEL: % d: % m ", ngx_event_ident (ev-> data), ev-> timer. key); // remove the time-out event object ngx_rbtree_delete (& ngx_event_timer_rbtree, & ev-> timer) from the red/black tree; ngx_mutex_unlock (ngx_event_timer_mutex); # If (ngx_debug) ev-> timer. left = NULL; ev-> timer. right = NULL; ev-> timer. parent = NULL; # endif // Tag: whether to add to the red/black tree timeout management ev-> timer_set = 0; // Tag: whether to time out ev-> timedout = 1; // call the callback function ev-> handler (EV); continue;} break;} ngx_mutex_unlock (ngx_event_timer_mutex );}

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.