【Nginx】定时器事件
转自:烟雨江南
Nginx事件管理主要是网络事件和定时器事件。下面介绍定时器事件管理,即超时管理。
为什么进行超时管理?
Nginx有必要对可能发生超时的事件
进行统一管理,并在事件超时时作出相应的处理,比如回收资源,返回错误等。举例来说,当客户端对nginx发出请求连接后,nginx会
accept()并建立对应的连接对象、读取请求的头部信息。而读取这个头部信息显然是要在一定的时间内完成的。如果在一个有限的时间内没有读取到头部信息或者读取的头部信息不完整,那么nginx就无法进行正常处理,并且认为这是一个错误/非法的请求,直接返回错误信息并释放相应资源,如果
nginx不这样做,那么针对如此的恶意攻击就很容易实施。
如何进行超时管理?
对于超时管理,要解决两个问题:
nginx采用的是红黑树。
超时事件对象的组织
Nginx设置了两个全局变量以便在程序的任何地方都可以哀诉的访问到这棵红棵树(src/event/ngx_event_timer.c):
ngx_thread_volatile ngx_rbtree_t ngx_event_timer_rbtree;//超时管理的红黑树结构
static ngx_rbtree_node_t ngx_event_timer_sentinel;//红黑树中的哨兵节点
红黑树结构的初始化
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完成了红黑树结构的初始化:
/*
* 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, );
if (ngx_event_timer_mutex == NULL) {
return NGX_ERROR;
} #endif return NGX_OK;
}
而ngx_rbtree_init(tree, s, i) 是宏定义(src/core/ngx_rbtree.h),即新建了一棵空的红黑树:
#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()内,可以找到这样的代码:
void
ngx_http_init_connection(ngx_connection_t *c)
{
...
(358L)ngx_add_timer(rev, c->listening->post_accept_timeout);
...
}
ngx_add_timer的第一个参数是事件对象,第二个参数是超时时限。
超时检测:
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 = ; } else {
timer = ngx_event_find_timer();//将超时检测时间设置为最快发生超时的事件对象的超时时刻与当前时刻之差
flags = NGX_UPDATE_TIME;
...
(void) ngx_process_events(cycle, timer, flags);
...
}
为无限大。timer在函数ngx_process_events()内被用作事件机制被阻塞的最长时间。那么timer为无限大会不会导致事件处理机制
无限等待而超时事件得不到及时处理呢?不会。因为正常情况下事件处理机制会监控到某些I/O事件的发生。即便是服务器太闲,没有任何I/O事件发生,工作
进程也不会无限等待。因为工作进程一开始就设置好了一个定时器,这实现在初始化函数ngx_event_process_init()内,看代码:
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 / ;
itv.it_interval.tv_usec = (ngx_timer_resolution % ) * ;
itv.it_value.tv_sec = ngx_timer_resolution / ;
itv.it_value.tv_usec = (ngx_timer_resolution % ) * ; if (setitimer(ITIMER_REAL, &itv, NULL) == -) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"setitimer() failed");
}
....
}
回调函数ngx_timer_signal_handler:
static void
ngx_timer_signal_handler(int signo)
{
ngx_event_timer_alarm = ; #if 1
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, , "timer signal");
#endif
}
可以看出它仅仅是将标志变量ngx_event_timer_alarm 设置为1.
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 == -) ? ngx_errno : ; 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):
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)。
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 > ? timer : );
}
该函数从红黑树中找到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()函数会被执行。
测是否有事件对象超时不需遍历扫描所有超时时间对象,而是找到最近的即将超时的超时事件对象。判断其是否超时,如果超时,则将其移出红黑树,设置超时标
记,调用回调函数进行处理。之后再判断第二近的即将超时的超时事件对象,如此反复,知道某个超时事件对象还未超时或所有超时事件对象都已超时并处理完毕就
结束检测。
下面是其核心代码:
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) <= ) {
ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer)); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, ,
"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 = ;
//标记:是否超时
ev->timedout = ;
//调用回调函数
ev->handler(ev); continue;
}
break;
} ngx_mutex_unlock(ngx_event_timer_mutex);
}
【Nginx】定时器事件的更多相关文章
- Nginx事件管理之定时器事件
1. 缓存时间 1.1 管理 Nginx 中的每个进程都会单独地管理当前时间.ngx_time_t 结构体是缓存时间变量的类型: typedef struct { /* 格林威治时间1970年1月1日 ...
- NGINX 定时器
写在前面 写NGINX系列的随笔,一来总结学到的东西,二来记录下疑惑的地方,在接下来的学习过程中去解决疑惑. 也希望同样对NGINX感兴趣的朋友能够解答我的疑惑,或者共同探讨研究. 整个NGINX系列 ...
- 浅析 Nginx 网络事件
Nginx 是一个事件驱动的框架,所谓事件主要指的是网络事件,Nginx 每个网络连接会对应两个网络事件,一个读事件一个写事件.在深入了解 Nginx 各种原理及在极端场景下的一些错误场景处理时,需要 ...
- C# 定时器事件(设置时间间隔,间歇性执行某一函数,控制台程序)
定时器事件代码 static void Main(string[] args) { Method(); #region 定时器事件 Timer aTimer = new Timer(); aTimer ...
- 【转】C# 定时器事件(设置时间间隔,间歇性执行某一函数,控制台程序)
using System.Timers;定时器事件代码 static void Main(string[] args) { Method(); #region 定时器事件 Timer aTimer = ...
- 定时器事件QtimerEvent 随机数 qrand Qtimer定时器
QTimerEvent类:定时器事件.QObject的子类都可使用 int QObject::startTimer(int interval)[参数:毫秒][返回值:定时器整型编号]来开启一个定时器 ...
- QT_8_Qt中的事件处理_定时器事件_定时器类_事件分发器_事件过滤器_绘图事件_高级绘图事件_绘图设备_QFile 文件读写_QFileInfo文件信息
Qt中的事件处理 1.1. 捕获QLabel中是鼠标事件 1.2. enterevent 鼠标进入 1.3. leaveevent 鼠标离开 1.4. 鼠标按下MyLabel::mousePressE ...
- Qt事件系统之四:定时器事件与随机数
一.定时器事件和随机数 QTimerEvent类用来描述一个定时器事件.对于一个QObject的子类,只需要使用int QObject::startTimer ( int interval)函数来开启 ...
- Nginx的事件循环
首先事件循环的起点就是监听端口获取连接,我们可以在ngx_event_core_module模块的ngx_event_process_init函数中看到如下的代码: /* for each liste ...
随机推荐
- JAVA基础——网络编程之网络链接
一.网络编程基本概念 1.OSI与TCP/IP体系模型 2.IP和端口 解决了文章最开始提到的定位的问题. IP在互联网中能唯一标识一台计算机,是每一台计算机的唯一标识(身份证):网络编程是和远程计算 ...
- openjudge-1664 放苹果
总时间限制: 1000ms 内存限制: 65536kB 描述 把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法. 输 ...
- selenium——操作滚动条
在自动化测试的过程中,难免会应用到翻页键,但是webdriver提供的方法都是操作当前页面可见的元素,对于未在当前范围展示的翻页键,该如何操作呢? 小编在这里介绍一种方法:使用JavaScript操作 ...
- Linux基础学习-RPM
目录 1. RPM简介 2. 常用软件安装工具 3. RPM命令参数 4. 使用范例 4.1 查看rpm包信息 4.2 查看rpm包内容 4.3 查看rpm包依赖 4.4 安装rpm包 4.5 卸载r ...
- Saving James Bond - Easy Version 原创 2017年11月23日 13:07:33
06-图2 Saving James Bond - Easy Version(25 分) This time let us consider the situation in the movie &q ...
- [jzoj5073 GDOI2017第二轮模拟] 影魔
Description 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样的灵魂,包括诗人.牧师.帝王.乞丐.奴隶.罪人,当然,还有英雄.每一个灵 ...
- 关于自由拖拽完成的剪切区域(UI组件之图片剪切器)
var x, y,areaWidth,areaHeight; var down;//闪光的判断标准,很好 addEvent(canvas,'mousedown',function(e){ // con ...
- Java学习之分支结构---判断语句:if语句和switch语句
一个if语句包含一个布尔表达式和一条或多条语句,if 语句的用语法如下:if 语句 if(布尔表达式) { //如果布尔表达式为true将执行的语句 },如果布尔表达式的值为 true,则执行 if ...
- metadata的使用以及简单的orm模式
使用sqllite3和metadata简单的封装了个简单的orm #!/usr/bim/python #-*-coding: utf-8 -*- import threading import sql ...
- jQuery 1.9升级指南
http://www.css88.com/archives/5086 原文地址:http://jquery.com/upgrade-guide/1.9/ 翻译的不对或者不通顺的地方欢迎拍砖留言。 概述 ...