nginx自定义upstream模块需手动实现三类超时(connect/send/read)及重试逻辑:分别在对应事件回调中调用ngx_http_upstream_next,配置next_upstream标志与重试次数,并注意幂等性、定时器管理与状态同步。

在 Nginx 模块开发中,后端超时与重试不是靠简单配置就能完全控制的,尤其当你编写自定义 upstream 模块(如对接私有协议或非 HTTP 后端)时,需主动介入请求生命周期,在关键回调中实现超时判定、连接/读写失败捕获、以及重试决策逻辑。
理解 Nginx upstream 的超时分层机制
Nginx 对 upstream 的超时分为三类,必须在模块中分别处理:
-
connect_timeout:建立 TCP 连接的时限。在
ngx_http_upstream_init_request后触发ngx_event_connect_peer,若连接未就绪且超时,会调用upstream->peer->failed并进入重试流程; -
send_timeout:发送请求数据到后端的时限。需在
ngx_http_upstream_send_request中设置wev->handler,并在超时事件回调中调用ngx_http_upstream_next; -
read_timeout:等待后端响应的时限。在
ngx_http_upstream_process_header前,通过rev->handler绑定读事件,并在超时回调中终止当前连接、标记 peer 失败、触发重试。
在 upstream 模块中启用并控制重试行为
重试不是自动发生的,它依赖于 upstream 框架的状态机流转。关键点包括:
- 调用
ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT)或带其他错误标志(如NGX_HTTP_UPSTREAM_FT_ERROR),框架才会尝试下一个 peer; - 确保
u->conf->next_upstream已启用对应错误类型(例如NGX_HTTP_UPSTREAM_FT_TIMEOUT),否则即使调用next也不会重试; - 重试次数受
u->conf->next_upstream_tries限制,默认为 0(不限),建议设为合理值(如 2~3),避免雪崩; - 若使用自定义 peer 获取逻辑(如
get回调),需在重试前调用ngx_http_upstream_rr_peer_free释放当前失败 peer,并由框架重新调用get获取下一个。
手动管理连接状态与超时定时器
对于非标准协议(如自定义二进制协议),Nginx 默认的 HTTP 超时逻辑不适用,需自行维护:
- 在
ngx_http_upstream_handler中启动自定义定时器:ngx_add_timer(rev, u->conf->read_timeout); - 在事件 handler(如
ngx_http_myproto_read_handler)开头检查rev->timedout,若为真则清理连接、调用ngx_http_upstream_next; - 每次成功读/写后务必调用
ngx_del_timer(rev)和ngx_add_timer(rev, ...)重置超时,防止“假超时”; - 注意:定时器必须在事件循环上下文中添加,不能在子线程或异步回调中直接调用
ngx_add_timer。
避免重试陷阱:幂等性与状态同步
盲目重试可能引发重复提交或状态不一致,尤其在非 GET 请求场景:
- 仅对幂等请求(如 GET、HEAD、OPTIONS)默认开启重试;对 POST/PUT 等操作,应通过
u->conf->next_upstream &= ~NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT显式禁用,或在重试前检查请求体是否已发送; - 若模块支持请求体缓冲(
u->request_bufs),重试前需确保缓冲可复用(如未被释放或移动); - 当后端返回部分响应(如 header 已收但 body 流中断),不应重试整个请求,而应走错误处理而非
next;可通过u->state->status和u->buffer.pos判断响应完整性。










