nginx超时事件的处理
来源:互联网 发布:网络机顶盒安装视频 编辑:程序博客网 时间:2024/05/17 22:54
几乎每个比较大型的系统都会有自己的一套超时事件的处理方式,nginx也不例外。nginx出于高性能的考虑,使用红黑树来管理超时的事件。这里红黑树的原理在网络中有很多文档去介绍,这里就不讲述红黑树的原理了。这篇博客值讲述nginx是怎么把事件与红黑树联系到一起的。大家都知道nginx处理事件有一个结构体:
typedef struct ngx_event_s ngx_event_tstruct ngx_event_s {......ngx_rbtree_node_t timer;......}nginx就是通过在ngx_event_s中添加一个timer成员,与红黑树联系到一起的。讲述事件超时的处理就不得不提到两个全局变量:
ngx_thread_volatile ngx_rbtree_t ngx_event_timer_rbtree; //整棵红黑树结构static ngx_rbtree_node_t ngx_event_timer_sentinel; //属于红黑树的一个节点,在红黑树的操作中被当作哨兵借点使用事件超时的初始化是通过 ngx_event_timer_init进行的
ngx_int_tngx_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;}这块代码也比较简单,主要是调用nginx红黑树的接口来进行初始化。而#if和#endif中的那块是关于多线程的代码,由于nginx到现在还没有用到多线程,这块代码可以直接无视。ngx_event_timer_init是通过ngx_event_process_init调用的。这就是说每个子进程都会有自己的事件超时管理。
事件超时初始化已经完成了,那怎么才能把事件插入到红黑树中进行管理呢?这就需要介绍一个函数ngx_event_add_timer:
static ngx_inline voidngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer){ ngx_msec_t key; ngx_msec_int_t diff; key = ngx_current_msec + timer;..... ev->timer.key = key; ngx_mutex_lock(ngx_event_timer_mutex); ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer); ngx_mutex_unlock(ngx_event_timer_mutex); ev->timer_set = 1;}这个函数的代码也是比较简单的,也无需多说了。到现在,nginx事件超时如何初始化,如何把事件插入到红黑树中进行管理都讲到了。但是可能到现在还是有点迷糊就是nginx是怎么串联到整个系统中的呢?下面来讲述。
nginx工作进程都是通过ngx_process_events_and_timers函数来进行处理事件的。ngx_process_events_and_timers会选择io复用的方式,以epoll为例。nginx会阻塞在epoll_wait这个调用中。如果有事件发生就会返回并进行处理;如果没有事件返回,那epoll就会在指定的时间内返回。现在的问题是这个时间是多少呢?请看ngx_process_events_and_timers:
voidngx_process_events_and_timers(ngx_cycle_t *cycle){...... if (ngx_timer_resolution) { timer = NGX_TIMER_INFINITE; flags = 0; } else { timer = ngx_event_find_timer(); flags = NGX_UPDATE_TIME;#if (NGX_THREADS) if (timer == NGX_TIMER_INFINITE || timer > 500) { timer = 500; }#endif }...... delta = ngx_current_msec; (void) ngx_process_events(cycle, timer, flags); delta = ngx_current_msec - delta;...... if (delta) { ngx_event_expire_timers(); }......}epoll_wait等待的时间会根据全局变量ngx_timer_resolution来设定,如果ngx_timer_resolution有值就会把epoll_wait的时间设为-1,并且flags设为0.至于flags有什么作用等下再说。如果ngx_timer_resolution没有值,就会从红黑树从搜索即将发生超时事件的超时事件为timer的值,同样也会设置flags的值。继续向下看,delta会计算出epoll_wait处理的时间。如果delta为0就不会去处理nginx中红黑树的超时事件。下面来看下epoll_wait的那快代码:
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(); }......}从那句if语句可以看到如果flags设置了更新时间或者ngx_event_timer_alarm不为0就会更新ngx_current_sec的值,这样在ngx_process_events_and_timers的delta就不可能为0,这样就会去处理红黑树中的超时事件。
但是如果既没有事件发生而且timer为-1,那nginx岂不是要永远阻塞在epoll_wait上,这样超时的事件永远得不到处理。真的是这样吗?当然不是,这就是为什么上面判断if的时候还有一个ngx_event_timer_alarm的原因。请看ngx_event_process_init:
static ngx_int_tngx_event_process_init(ngx_cycle_t *cycle){......if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) { struct sigaction sa; struct itimerval itv; ngx_memzero(&sa, sizeof(struct sigaction)); sa.sa_handler = ngx_timer_signal_handler; sigemptyset(&sa.sa_mask); if (sigaction(SIGALRM, &sa, NULL) == -1) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "sigaction(SIGALRM) failed"); return NGX_ERROR; } 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_resolution毫秒,这样就算事件发生,epoll_wait的时间为-1.还是会被这个信号事件打断。我们来看下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}刚好就是我们在epoll_wait需要的结果。这样就解决了nginx那个永远阻塞在epoll_wait上的问题。
在分析源码的时候,我们看到ngx_timer_resolution好几次了,那这个变量的值从哪来的呢?这个是通过timer_resolution这个命令配置在配置文件中的。
自此nginx超时事件的处理已经讲完。
- nginx超时事件的处理
- nginx的超时处理
- Nginx的超时处理
- nginx的超时处理
- Nginx的超时处理
- nginx的事件处理
- 文章10:Nginx的超时处理
- 文章10:Nginx的超时处理
- Session超时后的事件监听处理
- Flink CEP 对超时事件的处理
- 反应式处理超时事件
- Nginx的事件处理机制
- Nginx的事件处理机制
- Nginx的事件处理机制
- Nginx 的事件处理机制
- nginx源码阅读(十二).定时器及超时事件的管理
- Sencha touch 2 store 处理超时的处理事件
- 对于nginx的思考2---事件处理
- 感觉不对
- hadoop基准性能测试
- 泛泰A800S移植A850天气软件debug流程
- BIRT初探
- C++ Primer学习笔记(一):第1、2章
- nginx超时事件的处理
- URLRewrite实现url地址伪静态化
- android之List<T>的空指针问题_List的初始化
- SqlHelper 泛型方法,反射,支持实体类
- URAL 1939 First Seal 解题报告
- 南阳理工-3-多边形重心问题
- 栈的push、pop序列
- 浙江大学PAT上机题解析之1008. Elevator (20)
- MiniGUI在HI3515上的移植