设为首页 收藏本站
查看: 1482|回复: 6

[经验分享] Nginx学习之十-超时管理(定时器事件)

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2013-7-11 09:59:10 | 显示全部楼层 |阅读模式

Nginx事件管理主要是网络事件和定时器事件。下面介绍定时器事件管理,即超时管理。


为什么要进行超时管理

Nginx有必要对可能发生超时的事件进行统一管理,并在事件超时时作出相应的处理,比如回收资源,返回错误等。举例来说,当客户端对nginx发出请求连接后,nginx机会accept()并建立对应的连接对象、读取额护短请求的头部信息。而读取这个头部信息显然是要在一定的时间内完成的。如果在一个有限的时间内没有读取到头部信息或者读取读取的头部信息不完整,那么nginx就无法进行正常处理,并且认为这是一个错误/非法的请求,直接返回错误信息并释放相应资源,如果nginx不这样做,那么针对如此的恶意攻击就和容易实施。


如何进行超时管理
对于超时管理,要解决两个问题:
(1)超时事件对象的组织
nginx采用的是红黑树。

(2)超时事件对象的超时检测
有两种方案:
一种是定时检测机制,通过设置定时器,没过一定时间就对红黑树管理的所有超时时间进行一次超级扫描。
另一种是先计算出距离当前最快发生超时的时间是多久。然后等待这个时间之后再去进行一次超时检测。

超时事件对象的组织nginx中封装事件的结构体ngx_event_s中有几个字段与超时管理联系紧密:
[Shell] 纯文本查看 复制代码
[/font][/color]

//超时  
unsigned         timedout:1;  
//事件存在于定时器中  
unsigned         timer_set:1;  
//定时器节点  
ngx_rbtree_node_t   timer;  

Nginx设置了两个全局变量以便在程序的任何地方都可以哀诉的访问到这棵红棵树(src/event/ngx_event_timer.c):

[Shell] 纯文本查看 复制代码
[/font][/color][/p]
ngx_thread_volatile ngx_rbtree_t  ngx_event_timer_rbtree;//超时管理的红黑树结构  
static ngx_rbtree_node_t          ngx_event_timer_sentinel;//红黑树中的哨兵节点
  •   



红黑树结构的初始化
红黑树的初始化函数ngx_event_timer_init()是在函数ngx_event_process_init()函数内被调用的。即每个worker进程会在自身的初始化时建立这棵红黑树。
源代码(src/eventngx_event.c):
[Shell] 纯文本查看 复制代码
[/font][/color]

static ngx_int_t  
ngx_event_process_init(ngx_cycle_t *cycle)  
{  
    ....  
    if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {  
        return NGX_ERROR;  
    }  
    ...  
}  

函数ngx_event_timer_init完成了红黑树结构的初始化:
[Shell] 纯文本查看 复制代码

/* 
 * the event timer rbtree may contain the duplicate keys, however, 
 * it should not be a problem, because we use the rbtree to find 
 * a minimum timer value only 
 */  
  
ngx_int_t  
ngx_event_timer_init(ngx_log_t *log)  
{  
    //红黑树初始化  
    ngx_rbtree_init(&ngx_event_timer_rbtree, &ngx_event_timer_sentinel,  
                    ngx_rbtree_insert_timer_value);  
  
    //多线程处理  
#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;  
}  

而ngx_rbtree_init(tree, s, i) 是宏定义(src/core/ngx_rbtree.h),即新建了一棵空的红黑树:
[Shell] 纯文本查看 复制代码

#define ngx_rbtree_init(tree, s, i)                                           \  
    ngx_rbtree_sentinel_init(s);                                              \  
    (tree)->root = s;                                                         \  
    (tree)->sentinel = s;                                                     \  
    (tree)->insert = i  


对事件进行超时监控当需要对某个事件进行超时监控时,就会把它加入到这个红黑树内。比如,nginx调用accept接受到客户端的请求并建立对应的连接对象connection后,在连接对象的初始化函数ngx_http_init_connection()内,可以找到这样的代码:
[Shell] 纯文本查看 复制代码
[/font][/color]

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

ngx_add_timer的第一个参数是事件对象,第二个参数是超时时限。


超时检测Nginx具体使用哪一种超时检测方案取决于配置项timer_resolution,比如:
timer_resolution 100ms;
反应到nginx代码中就是全局变量ngx_timer_resolution的值为100;
下面我们看worker进程的核心处理函数ngx_process_events_and_timers(ngx_event.c):
[Shell] 纯文本查看 复制代码
[/font][/color]

void  
ngx_process_events_and_timers(ngx_cycle_t *cycle)  
{  
    ngx_uint_t  flags;  
    ngx_msec_t  timer, delta;  
  
    if (ngx_timer_resolution) {  
        timer = NGX_TIMER_INFINITE;  
        flags = 0;  
  
    } else {  
        timer = ngx_event_find_timer();//将超时检测时间设置为最快发生超时的事件对象的超时时刻与当前时刻之差  
        flags = NGX_UPDATE_TIME;  
...  
(void) ngx_process_events(cycle, timer, flags);  
...  
}  

可以看到,ngx_timer_resolution是否为0影响了两个值timer和flags。


当ngx_timer_resolution不为0时,即方案1.timer为无限大。timer在函数ngx_process_events()内被用作事件机制被阻塞的最长时间。那么timer为无限大会不会导致事件处理机制无限等待而超时事件得不到及时处理呢?不会。因为正常情况下事件处理机制会监控到某些I/O事件的发生。即便是服务器太闲,没有任何I/O事件发生,工作进程也不会无限等待。因为工作进程一开始就设置好了一个定时器,这实现在初始化函数ngx_event_process_init()内,看代码:
[Shell] 纯文本查看 复制代码
[/font][/color]

static ngx_int_t  
ngx_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");  
        }  
        ....  
}  

回调函数ngx_timer_signal_handler:
[Shell] 纯文本查看 复制代码

static void  
ngx_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  
}  


可以看出它仅仅是将标志变量ngx_event_timer_alarm 设置为1.
只有在ngx_event_timer_alarm 为1 的情况下,工作进程才会更新时间。函数ngx_epoll_process_events中的代码(ngx_epoll_module.c):
[Shell] 纯文本查看 复制代码
[/font][/color]

static ngx_int_t  
ngx_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();  
    }  
...  
} 



在方案一的情况下,||前面的式子为假,那么ngx_event_timer_alarm 不为1 的情况下,更新函数ngx_time_update()不会被执行。那么会导致超时检测函数ngx_event_expire_timers不会被执行。看ngx_process_events_and_timers函数的代码(ngx_event.c):
[Shell] 纯文本查看 复制代码

void  
ngx_process_events_and_timers(ngx_cycle_t *cycle)  
{  
...  
    delta = ngx_current_msec;  
    (void) ngx_process_events(cycle, timer, flags);//事件处理函数  
    delta = ngx_current_msec - delta;  
...  
if (delta) {  
        ngx_event_expire_timers();//超时检测函数  
    }  
...  
}  

当ngx_timer_resolution为0时,执行方案2。timer设置为最快发生超时的事件对象的超时时刻与当前时刻的时间差。具体计算时在函数ngx_event_find_timer内(ngx_event_timer.c)。
[Shell] 纯文本查看 复制代码

ngx_msec_t  
ngx_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);  
}  


该函数从红黑树中找到key值最小的节点,然后用key值减去当前时刻即得到预期timer值。这个值可能是负数,表示已经有事件超时了。因此直接将其设置为0.那么事件处理 机制在开始监控I/O事件时会立即返回,以便马上处理这些超时事件。同时flags被设置为NGX_UPDATE_TIME。从ngx_epoll_process_events函数的代码中可以看出ngx_time_update()将被执行,事件被更新。即事件处理机制每次返回都会更新时间。如果I/O事件比较多,那么会导致比较频繁地调用gettimeofday()系统函数,这也可以说是超时检测方案2对性能的最大影响。这个时候超时检测函数ngx_event_expire_timers()函数会被执行。


下面看看超时检测函数ngx_event_expire_timers。其完成的主要任务是对超时事件对象是否超时进行扫描检测以及对已超时事件对象的处理。
检测是否有事件对象超时不需遍历扫描所有超时时间对象,而是找到最近的即将超时的超时事件对象。判断其是否超时,如果超时,则将其移出红黑树,设置超时标记,调用回调函数进行处理。之后再判断第二近的即将超时的超时事件对象,如此反复,知道某个超时事件对象还未超时或所有超时事件对象都已超时并处理完毕就结束检测。
此函数流程图:
Center.jpg

下面是其核心代码:

[Shell] 纯文本查看 复制代码
[/font][/color][/p]
void  
ngx_event_expire_timers(void)  
{  
    ngx_event_t        *ev;  
    ngx_rbtree_node_t  *node, *root, *sentinel;  
  
    sentinel = ngx_event_timer_rbtree.sentinel;  
    //循环检测  
    for ( ;; ) {  
        ngx_mutex_lock(ngx_event_timer_mutex);  
        root = ngx_event_timer_rbtree.root;  
        if (root == sentinel) {  
            return;  
        }  
  
        //找到最近的即将超时的超时事件对象  
        node = ngx_rbtree_min(root, sentinel);  
  
        /* node->key <= ngx_current_time */  
        //如果已经超时  
        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);  
  
            //从红黑树中移除这个已超时的超时事件对象  
            ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);  
            ngx_mutex_unlock(ngx_event_timer_mutex);  
  
#if (NGX_DEBUG)  
            ev->timer.left = NULL;  
            ev->timer.right = NULL;  
            ev->timer.parent = NULL;  
#endif  
  
            //标记:是否已加入红黑树超时管理  
            ev->timer_set = 0;  
            //标记:是否超时  
            ev->timedout = 1;  
            //调用回调函数  
            ev->handler(ev);  
  
            continue;  
        }  
        break;  
    }  
  
    ngx_mutex_unlock(ngx_event_timer_mutex);  
}  








运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-7394-1-1.html 上篇帖子: Nginx学习之九-负载均衡(客户端请求与Nginx进程间负载均衡) 下篇帖子: Linux下Nginx+Tomcat整合的安装与配置 定时器

尚未签到

发表于 2013-7-15 12:39:36 | 显示全部楼层
没看完~~~~~~ 先顶,好同志

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

尚未签到

发表于 2013-7-19 22:34:03 | 显示全部楼层
好好 学习了 确实不错

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

尚未签到

发表于 2013-7-23 15:54:29 | 显示全部楼层
我身在江湖,江湖里却没有我得传说。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

尚未签到

发表于 2013-7-28 13:34:28 | 显示全部楼层
沙发!沙发!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

尚未签到

发表于 2013-8-2 18:40:42 | 显示全部楼层
穿别人的鞋,走自己的路,让他们找去吧。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

尚未签到

发表于 2013-8-8 20:10:29 | 显示全部楼层
不要在一棵树上吊死,在附近几棵树上多试试死几次~

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表