Nginx模块干预upstream创建与初始化需在postconfiguration钩子中操作:解析阶段通过重写init_upstream挂载自定义peer_data,初始化阶段替换init_peer实现动态选点,请求时通过create_request/reinit_request增强运行时逻辑,全程须注意内存安全与异步约束。

在模块中干预 Upstream 的创建与初始化过程,核心在于利用 Nginx 提供的模块钩子(hook)机制,在 upstream 配置解析和实例初始化的关键节点插入自定义逻辑。这通常用于实现动态 upstream、配置增强、服务发现集成或连接策略定制等场景。
在配置解析阶段介入:`ngx_http_upstream_add` 与 `postconfiguration`
Nginx 在读取配置时,会将 upstream 块解析为 ngx_http_upstream_srv_conf_t 结构,并调用 ngx_http_upstream_add() 注册服务器。模块可在 postconfiguration 钩子中遍历已注册的 upstream 块,获取其 srv_conf,并挂载自定义数据或重写行为。
- 使用
ngx_http_upstream_srv_conf_t->servers获取原始 server 列表,可在此基础上动态增删或替换ngx_http_upstream_server_t实例 - 通过
ngx_http_upstream_srv_conf_t->peer.init_upstream替换为自定义初始化函数,接管后续 peer 初始化流程 - 注意:此阶段配置尚未完全冻结,但不能修改核心字段(如 name),应使用
ngx_pcalloc分配新结构并谨慎赋值
在 peer 初始化阶段介入:重写 init_upstream 与 init_peer
Upstream 的实际负载均衡逻辑由 peer 模块驱动。标准流程是:init_upstream 构建 peer_data 上下文,init_peer 为每次请求准备具体 peer 选择器。
- 在
postconfiguration中,将uscf->peer.init_upstream = my_init_upstream,其中my_init_upstream分配并初始化自定义my_peer_data_t -
my_init_upstream内可加载服务发现数据、预热连接池、设置健康检查回调等 -
my_init_peer被每次请求调用,负责设置r->upstream->peer.get = my_get_peer等关键函数指针,实现运行时 peer 选择逻辑
在请求上下文绑定时注入逻辑:利用 create_request 与 reinit_request
虽然不属于 upstream 创建本身,但在 ngx_http_upstream_init_request 流程中,模块可通过重写 uscf->create_request 或监听 NGX_HTTP_UPSTREAM_INIT_REQUEST 阶段,对 upstream 实例进行运行时增强。
- 例如:在
create_request中根据请求头动态选择 upstream 名称,或注入自定义变量到r->upstream->conf - 若需支持连接复用或 session 粘性,可在
reinit_request中恢复上次请求的 peer 状态或更新权重缓存 - 所有操作必须保证线程安全(尤其在多 worker 场景),避免共享非原子变量
注意事项与典型陷阱
直接修改 upstream 行为易引发稳定性问题,需严格遵循 Nginx 内存生命周期和配置阶段约束。
- 不要在
init_upstream中分配长期存活的堆内存而不注册清理回调(应使用ngx_pool_cleanup_add) - 避免在
init_peer中执行阻塞操作(如同步 HTTP 请求),Nginx 是异步事件模型 - 若 upstream 名称由变量决定(如
set $backend "xxx"; proxy_pass http://$backend),则无法在配置期静态干预,需改用proxy_pass动态解析钩子(如ngx_http_proxy_create_request中解析变量后调用自定义 upstream 查找) - 调试时可启用
error_log debug;并关注upstream init、peer get相关日志,确认钩子是否被正确触发

















