用动态数组收集上游服务器配置
nginx 解析 upstream 块时,用 ngx_array_t 连续保存服务器条目;每遇到一条 server 指令便追加一个元素。
相关概念
工程问题#
配置中的后端数量事先不确定,解析阶段需要持续追加,初始化负载均衡时又需要方便地遍历所有服务器。
实际实现#
ngx_array_push 检查已用数量与容量。若数组紧邻内存池尾部且有空间,就原地增加一个槽;否则分配双倍容量、复制旧元素并更新 elts。
处理流程#
- 创建服务器动态数组。
- 解析 server 指令并申请尾部槽位。
- 容量不足时原地扩展或复制到更大存储。
- 初始化条目,供后续上游初始化读取。
源码片段#
固定版本 · 原文摘录src/core/ngx_array.crelease-1.26.3 · L74–L90
74 /* allocate a new array */7576 new = ngx_palloc(p, 2 * size);77 if (new == NULL) {78 return NULL;79 }8081 ngx_memcpy(new, a->elts, size);82 a->elts = new;83 a->nalloc *= 2;84 }85 }8687 elt = (u_char *) a->elts + a->size * a->nelts;88 a->nelts++;8990 return elt;ngx_array_push
新存储按现有容量的两倍申请,复制旧数据并替换 elts,再以 size × nelts 计算追加槽位。这里保留容量 nalloc 与元素数量 nelts 的区别,也说明扩容会改变数组元素的存储地址。
片段是数组已满且无法原地扩展的分支,随后返回新元素槽位;原地扩展条件在上方。真实调用者位于 ngx_http_upstream_server。
收益与取舍#
工程分析带来的收益
- 连续布局便于按下标遍历。
- 追加时自动管理容量,无需预先设定最终服务器数。
付出的代价
- 扩容可能执行线性复制,并改变元素存储地址。
- 扩容遗留的池内存通常随整个内存池释放。
可比较的选择
链表追加时无需整体搬迁,但逐项分配和指针遍历增加管理成本。
版本与适用范围#
对应配置解析阶段的服务器容器,不是请求分发算法。扩容不总是翻倍;持有元素指针的代码须注意 elts 搬迁。
参考来源#
源码、官方文档与原始论文01
nginx 1.26.3:upstream 创建数组并追加 server
源码release-1.26.3src/http/ngx_http_upstream.c · ngx_http_upstream; ngx_http_upstream_server
相关实现与比较#
4 个相关案例Linux IIO 的 kfifo 缓冲在采样进入内核与用户态读取之间暂存数据,按先进先出顺序交付采样记录。
附源码片段 · 内存管理
Linux BPF 验证器的 check_cfg 使用显式指令栈执行非递归 DFS,检查跳转目标、可达性及控制流结构。
附源码片段 · 图处理与编译
Git 把内存中的 index 条目指针保存在有序、可增长的数组中,按路径和合并 stage 二分查找位置。
附源码片段 · 索引与查找
Redis SDS 把字节长度与可用容量放进头部,追加按长度复制,正文允许包含零字节。
附源码片段 · 内存管理