百队竞技淬精英,数智筑盾护山河,第三届“长城杯”网数智安全大赛(防护赛)总决赛在福州顺利闭幕
2026/4/29 16:22:46
ngx_event_expire_timers 函数 定义在 ./nginx-1.24.0/src/event/ngx_event_timer.cvoidngx_event_expire_timers(void){ngx_event_t*ev;ngx_rbtree_node_t*node,*root,*sentinel;sentinel=ngx_event_timer_rbtree.sentinel;for(;;){root=ngx_event_timer_rbtree.root;if(root==sentinel){return;}node=ngx_rbtree_min(root,sentinel);/* node->key > ngx_current_msec */if((ngx_msec_int_t)(node->key-ngx_current_msec)>0){return;}ev=ngx_rbtree_data(node,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);#if(NGX_DEBUG)ev->timer.left=NULL;ev->timer.right=NULL;ev->timer.parent=NULL;#endifev->timer_set=0;ev->timedout=1;ev->handler(ev);}}ngx_event_expire_timers 函数 是 Nginx 事件驱动模型中的定时器超时处理函数。 它在每次事件循环中被调用, 负责从定时器红黑树中找出所有已经超时的事件, 将其从树中移除,设置超时标志, 然后调用对应的事件处理回调函数。1 局部变量 2 遍历定时器红黑树1 局部变量{ngx_event_t*ev;ngx_rbtree_node_t*node,*root,*sentinel;sentinel=ngx_event_timer_rbtree.sentinel;获取全局定时器红黑树 ngx_event_timer_rbtree 的哨兵节点并存入局部变量 sentinel2 遍历定时器红黑树for(;;){root=ngx_event_timer_rbtree.root;if(root==sentinel){return;}node=ngx_rbtree_min(root,sentinel);/* node->key > ngx_current_msec */if((ngx_msec_int_t)(node->key-ngx_current_msec)>0){return;}ev=ngx_rbtree_data(node,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);#if(NGX_DEBUG)ev->timer.left=NULL;ev->timer.right=NULL;ev->timer.parent=NULL;#endifev->timer_set=0;ev->timedout=1;ev->handler(ev);}}#1 开始无限循环。 目的是连续从红黑树中取出最小超时节点处理, 直到遇到尚未超时或树为空为止。 因为可能同时有多个定时器在同一毫秒内到期,必须在一次调用中全部处理完。#2 在每次循环开始时重新读取树的根节点指针 事件处理函数 ev->handler(ev) 内部可能会向定时器树中添加或删除其他节点, 导致树的根节点发生变更。因此这里重新获取以保证正确性#3 若根节点等于哨兵节点,说明定时器红黑树为空, 没有任何等待中的定时器事件,直接返回#4 调用 ngx_rbtree_min 获取以 root 为根的子树中键值最小的节点, 即最早超时的定时器节点。 红黑树的中序遍历顺序保证了最左侧节点键值最小,因此该节点对应最先到期的事件。#5 node->key 存储的是事件的绝对超时时间(单位毫秒,类型为 ngx_msec_t,无符号整数)。 ngx_current_msec 是 Nginx 缓存的当前近似毫秒时间(通常由 ngx_time_update() 更新)。 两者相减,并将结果强制转换为有符号整数 ngx_msec_int_t。 若差值 > 0,说明 node->key 仍然大于当前时间,即最早的定时器也尚未到达触发时刻, 因此无需处理任何事件,直接返回。 若差值 <= 0,说明该节点的超时时间已经抵达或超过,需要处理超时。#6 超时处理#6-1 通过 node 指针获取它所在的 ngx_event_t 结构体。 红黑树节点 timer 内嵌于 ngx_event_t 中,该宏利用偏移量计算得到父结构体指针 ev。#6-2 输出调试日志#6-3 将该事件的定时器节点从全局定时器红黑树中删除。 此后此事件不再受定时器树管理。#6-4 如果编译时定义了 NGX_DEBUG,则将树节点内的三个指针清空。 目的是在调试版本中及早发现悬挂指针的非预期访问。#6-5-1 将事件的 timer_set 标志置为 0, 表示该事件已不在定时器红黑树中。 Nginx 很多地方依赖该标志来判断一个事件是否已经在定时器中,防止重复添加或重复删除#6-5-2 设置事件的超时标志 timedout 为 1, 表示该事件是因为超时被触发的。 事件的处理函数 handler 内可以通过该标志区分是正常 I/O 事件还是超时事件。#6-7 调用该事件注册的处理函数 handler,并将事件自身作为参数传入。 处理函数将根据 timedout == 1 执行超时逻辑,例如关闭连接、重试操作或释放资源。 这个回调有可能向定时器树添加新的定时器或删除其他定时器, 但因为函数会继续循环,能够正确处理本次循环后续仍需触发的到期事件。