ngx_drain_connections
1 定义ngx_drain_connections 函数 定义在 ./nginx-1.24.0/src/core/ngx_connection.cstaticvoidngx_drain_connections(ngx_cycle_t*cycle){ngx_uint_ti,n;ngx_queue_t*q;ngx_connection_t*c;if(cycle-free_connection_ncycle-connection_n/16||cycle-reusable_connections_n0){return;}if(cycle-connections_reuse_time!ngx_time()){cycle-connections_reuse_timengx_time();ngx_log_error(NGX_LOG_WARN,cycle-log,0,%ui worker_connections are not enough, reusing connections,cycle-connection_n);}cNULL;nngx_max(ngx_min(32,cycle-reusable_connections_n/8),1);for(i0;in;i){if(ngx_queue_empty(cycle-reusable_connections_queue)){break;}qngx_queue_last(cycle-reusable_connections_queue);cngx_queue_data(q,ngx_connection_t,queue);ngx_log_debug0(NGX_LOG_DEBUG_CORE,c-log,0,reusing connection);c-close1;c-read-handler(c-read);}if(cycle-free_connection_n0cc-reusable){/* * if no connections were freed, try to reuse the last * connection again: this should free it as long as * previous reuse moved it to lingering close */ngx_log_debug0(NGX_LOG_DEBUG_CORE,c-log,0,reusing connection again);c-close1;c-read-handler(c-read);}}ngx_drain_connections 函数的作用是 当服务器面临大量并发连接、空闲连接即将耗尽时 它通过主动关闭一些处于可复用状态reusable的连接来释放资源从而保证新连接的正常接入。2 详解1 函数签名staticvoidngx_drain_connections(ngx_cycle_t*cycle)该函数不向调用者返回任何值 参数 ngx_cycle_t *cycle 指向当前运行周期上下文2 逻辑流程1 局部变量 2 资源状况判断 3 日志 4 回收可复用连接 5 重试1 局部变量{ngx_uint_ti,n;ngx_queue_t*q;ngx_connection_t*c;i循环计数器。 n本次计划复用的连接数量。 q指向队列节点的指针用于操作双向链表。 c指向具体连接对象的指针。2 资源状况判断if(cycle-free_connection_ncycle-connection_n/16||cycle-reusable_connections_n0){return;}cycle-free_connection_n当前进程空闲连接对象的数量。 cycle-connection_n该进程分配的总连接数由 worker_connections 指令指定。 若空闲连接数大于总连接数的 1/16说明资源尚不紧张 或者当前没有任何可复用连接在队列中。 这两种情况下均无需执行回收操作直接返回。 意义设定一个回收阈值约 93.75% 连接被占用防止频繁触发连接回收。3 日志if(cycle-connections_reuse_time!ngx_time()){cycle-connections_reuse_timengx_time();ngx_log_error(NGX_LOG_WARN,cycle-log,0,%ui worker_connections are not enough, reusing connections,cycle-connection_n);}ngx_time() 获取当前系统时间的秒级时间戳缓存值非系统调用。 connections_reuse_time 记录上次打印警告日志的时间戳。 逻辑如果与上次打印日志的时间不在同一秒内即每秒最多打印一次则更新时间为当前秒 并输出一条警告日志提示 worker_connections 配置不足正在复用连接。 避免日志刷屏同时提醒运维人员当前连接数配置可能偏小。4 回收可复用连接cNULL;nngx_max(ngx_min(32,cycle-reusable_connections_n/8),1);初始化 c 为 NULL后续用于保存最后一次处理的连接。 计算本次要回收的连接数量 n cycle-reusable_connections_n / 8取可复用连接总数的 1/8。 ngx_min(32, ...)与 32 比较取较小值单次回收最多不超过 32 个连接。 ngx_max(..., 1)确保至少回收 1 个连接。 意义 采用分批少量回收的策略避免一次性关闭过多连接造成性能抖动 同时当可复用连接极少时也保证能回收至少一个。for(i0;in;i){if(ngx_queue_empty(cycle-reusable_connections_queue)){break;}循环 n 次尝试回收指定数量的连接。 每次循环前检查 reusable_connections_queue 是否为空 若队列已空则提前结束循环qngx_queue_last(cycle-reusable_connections_queue);cngx_queue_data(q,ngx_connection_t,queue);ngx_queue_last() 获取双向链表的尾部节点指针ngx_queue_t。 ngx_queue_data 通过队列节点地址、结构体类型以及成员名 queue反推出包含该节点的 ngx_connection_t 结构体指针。 逻辑 从队列尾部取出一个连接对象。 每次有新连接变为可复用状态时它都被插到 链表头部最新位置 所以尾部连接是 进入可复用状态 时间最久的连接优先回收ngx_log_debug0(NGX_LOG_DEBUG_CORE,c-log,0,reusing connection);如果编译时启用了 --with-debug 此处会输出调试日志记录正在复用关闭该连接。c-close1;c-read-handler(c-read);}c-close 1 设置连接的关闭标志位告知后续处理流程该连接即将被关闭。 c-read-handler(c-read) 直接调用该连接读事件的回调函数。 同步触发读事件处理器 直接调用该连接绑定的读事件回调如 ngx_http_keepalive_handler。 因为设置了 c-close1处理器内部会走关闭分支释放相关资源5 重试if(cycle-free_connection_n0cc-reusable){/* * if no connections were freed, try to reuse the last * connection again: this should free it as long as * previous reuse moved it to lingering close */ngx_log_debug0(NGX_LOG_DEBUG_CORE,c-log,0,reusing connection again);c-close1;c-read-handler(c-read);}}#1 在完成上述 n 次回收尝试后再次检查空闲连接数是否为 0即资源极端紧张。 同时确认 c 不为空即至少处理过一个连接且该连接仍然标记为 reusable。 隐含逻辑 经过上一轮处理某些连接可能并未真正被关闭例如因引用计数未清零或正在处理请求 因此若空闲连接仍为 0说明之前回收操作未能释放任何资源。#2 调试日志。#3 再次对同一个连接 c 执行关闭操作 试图强制将其从延迟关闭状态推进到最终释放状态 以腾出一个空闲连接槽位。 这是一种绝望尝试仅在资源极度匮乏且前序操作无效时发生。