ngx_list_t是Nginx为HTTP头部定制的块式链表,基于ngx_array_t实现,每块默认存4个元素,插入均摊O(1),内存由request pool统一管理,保证顺序性与局部性。

在 Nginx 源码中,ngx_list_t 是一个轻量、高效、专为 HTTP 头部字段设计的动态链表结构。它不用于通用链表场景,而是在请求解析阶段(如 ngx_http_parse_header_line)中,将解析出的头部名/值对按顺序存入,保证插入有序、内存局部性好、分配可控。
为什么用 ngx_list_t 而不用数组或哈希?
Nginx 的 HTTP 头部具有如下特点:数量不确定(少则几个,多则上百)、顺序敏感(如 Cookie 多个值需保持原始顺序)、写多读少(解析时插入为主,后续可能遍历或查找)。ngx_list_t 正是为此定制:
- 底层基于
ngx_array_t实现“块式链表”:每个节点是固定大小的数组(默认 4 个元素),新元素优先填满当前块,填满后才 malloc 新块,减少频繁小内存分配 - 插入 O(1) 均摊,遍历 O(n),无哈希冲突和重哈希开销,也不需要预估最大长度
- 所有内存由 request pool 统一管理,生命周期与请求绑定,无需手动释放
HTTP 头部处理中的典型使用路径
以 ngx_http_process_request_headers 为例,头部解析流程中 ngx_list_t 扮演核心容器角色:
- 请求结构体
ngx_http_request_t中定义headers_in.headers字段,类型即为ngx_list_t - 每次成功解析一行 Header(如
User-Agent: curl/8.5.0),就调用ngx_list_push(&r->headers_in.headers)分配一个ngx_table_elt_t节点,并拷贝 name/value 的字符串(指向原始缓冲区的只读切片) - 后续模块(如 rewrite、access、proxy)通过遍历该 list 获取特定 header,例如
ngx_http_find_header就是对该 list 的线性搜索封装
关键字段与内存布局要点
ngx_list_t 结构体本身极简,但理解其与 ngx_list_part_t 的协作是掌握其实战行为的关键:
-
last指向当前可插入位置(即最后一个已用元素的下一个地址) -
part是首块指针,类型为ngx_list_part_t *;每个 part 包含elts(数据起始)、nelts(当前元素数)、next(指向下一 part) - 所有字符串(
ngx_str_t中的data)均指向原始接收缓冲区(r->header_in),Nginx 不做深拷贝,靠 buffer 生命周期保障有效性 - 初始化时通过
ngx_list_init指定每块容量(如 headers_in 默认为 20),过小会导致频繁 alloc,过大浪费内存
实战调试与常见误区
在自定义模块中操作 headers_in.headers 时,需注意以下实践细节:
- 不要直接修改
part->elts内存,应始终使用ngx_list_push插入新项,否则破坏链表一致性 - 遍历时用
for (part = &list->part; part; part = part->next)+for (i = 0; i nelts; i++)双层循环,不可假设单块 - 若需快速查找某 header(如
Authorization),建议先用ngx_http_find_header,而非自己遍历 —— 它已做大小写忽略比较优化 - 添加自定义 header 时,务必调用
ngx_pnalloc在 request pool 中分配 name/value 内存,并正确设置len和data,避免悬垂指针











