ngx_http_upstream_init_main_conf 在配置加载末期、所有 upstream 块解析完毕后被调用,负责遍历 upstream 实例、触发 init_upstream 回调、初始化 peer 结构、校验 server 列表并预处理权重等。

ngx_http_upstream_init_main_conf 是 Nginx 在配置解析阶段调用的一个关键回调函数,它不负责创建 upstream 块本身,而是对已解析完成的 upstream 配置进行“二次初始化”——即填充默认值、校验逻辑、预分配资源、建立内部数据结构关联等。理解它,是掌握 Nginx upstream 模块初始化流程的核心一环。
它在什么时候被调用?
该函数由 Nginx 核心在 配置加载末期、所有 server 和 upstream 块都已解析完毕后 统一调用,属于 ngx_http_module_t 的 init_main_conf 钩子。此时:
- 所有
upstream { ... }块已被解析为ngx_http_upstream_srv_conf_t实例,并挂入全局配置数组; - 但各 upstream 的内部结构(如 peer 初始化函数、负载均衡上下文、server 数组等)尚未就绪;
- 模块间依赖关系(如 upstream 与 upstream_ip_hash、upstream_least_conn 等子模块)需要在此统一协调。
它主要做哪些事?
以开源 Nginx 1.24+ 为例,ngx_http_upstream_init_main_conf 的核心职责包括:
-
遍历所有 upstream srv_conf:通过
cf->cycle->conf_ctx[ngx_http_module.ctx_index]获取 HTTP 模块配置上下文,再取出所有 upstream 块对应的ngx_http_upstream_srv_conf_t *; -
触发每个 upstream 的 init_upstream 回调:若用户显式指定了负载均衡方式(如
least_conn、ip_hash),则调用对应模块注册的init_upstream函数(如ngx_http_upstream_init_least_conn);否则使用默认的轮询(ngx_http_upstream_init_round_robin); -
初始化 peer 数据结构:为每个 upstream 分配并初始化
peer.init、peer.data,设置后续请求选 peer 时使用的函数指针(如get_peer,free_peer); -
校验 server 列表有效性:检查是否至少有一个有效 server(非 backup 或 resolve 类型未生效),否则报错
"no servers are inside upstream"; - 预处理 server 权重与状态:计算总权重、归一化、构建一致性哈希环(如启用 hash 指令)、初始化 shared memory zone(如需共享状态)。
常见问题与调试线索
如果你在自定义 upstream 模块或调试 upstream 行为时遇到问题,可重点关注以下位置:
- 模块未注册
init_upstream回调 → 导致 fallback 到 round_robin,即使你写了别的逻辑; - 在
init_upstream中访问了未初始化的uscf->servers(应确保它非 NULL 且已解析); - 多个 upstream 共享同一段内存 zone 但未加锁 → 出现在 init_main_conf 阶段并发调用时(实际单线程,但需注意多 worker 共享结构的初始化时机);
- 错误日志中出现
"upstream \"xxx\" has no servers"→ 说明init_main_conf遍历时发现uscf->servers == NULL或全为无效 server。
如何验证你的 upstream 初始化逻辑?
最直接的方式是在自定义模块的 init_upstream 函数开头加日志:
static ngx_int_t
my_upstream_init(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *uscf) {
ngx_log_error(NGX_LOG_NOTICE, cf->log, 0,
"my_upstream_init called for upstream \"%V\"", &uscf->host);
return ngx_http_upstream_init_round_robin(cf, uscf);
}
然后 reload Nginx,观察 error.log —— 正常情况下,每个 upstream 名称都会被打印一次,且发生在所有 server{} 解析完成后、worker 启动前。

















