HTTP/2 Server Push 在 Nginx 中启用后,每条连接都会维护一份推送记录,用于判断某个资源是否已经向该连接推送过。随着长连接数量增加,这些记录会不断累积,相应的计数也会线性增长。如果不主动干预,计数器只增不减,最终可能影响推送去重逻辑,甚至让内存使用持续走高。http2_push_diary_reset 就是用来主动清零这个计数器的配置项。它不会删除已经推送出去的资源文件,只会把内部记录状态重置,使后续请求重新按照规则判断是否需要推送。

HTTP/2 Server Push 的计数器从哪来
Nginx 的 HTTP/2 模块在开启 Server Push 时,会为每个 HTTP/2 连接建立一个 push diary 结构。这个结构可以理解成一张连接级的小型记录表,记录已经通过该连接推送过的资源 URI 或者对应的哈希值。每当客户端请求一个主文档时,Nginx 会根据 http2_push 指令或者响应头中的 Link 预加载提示读取候选资源,然后进入去重判断:如果资源已经存在于 push diary 中,就跳过推送;如果不存在,则执行推送,并把该资源加入 diary,同时计数器加一。
这种计数不是全局的,它更接近连接维度。也就是说,同一个 worker 下可能有成千上万个活跃连接,每个连接都有自己的 diary 计数。计数本身不会自动下降,即便客户端已经离开,某些实现中对应的结构会在连接关闭时释放,但在长连接、慢连接或者大量并发的场景里,仍然会出现单连接计数快速累积的情况。尤其是在资源数量很多、页面不断加载新模块时,diary 中的条目可能远超预期,去重查找的成本也会随之增加。
理解了计数来源之后,就能明白重置的意义:它不是为了修改 Nginx 的业务逻辑,而是为了在合适的时机清理连接内部已经过期的记录。这样可以让后续推送判断回到一个更干净的状态,避免因旧记录过多导致查找效率下降或内存膨胀。
http2_push_diary_reset 的语法与配置方式
http2_push_diary_reset 通常以参数形式指定一个触发重置的阈值,例如设置为 300,表示当当前连接的 push diary 计数达到 300 条时,自动清空记录并将计数归零。它可以在 http、server 和 location 层级中使用,优先级遵循 Nginx 配置继承规则。下面是一个完整的 HTTPS HTTP/2 站点配置示例:
http {
server {
listen 443 ssl http2;
server_name www.ipipp.com;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
http2_push_preload on;
http2_push /assets/app.css;
http2_push /assets/app.js;
http2_push_diary_reset 300;
location / {
root /var/www/html;
index index.html;
}
}
}
上面的配置中,http2_push_diary_reset 300 表示单个 HTTP/2 连接上的推送记录数每达到 300 条,就执行一次重置。这里的 300 是一个经验值,并不是固定标准。如果站点的首屏资源只有十几个,那么这个阈值可以设得更高,例如 800 或 1000;如果页面会动态加载大量模块,建议把阈值降低到 100 至 200,以便更频繁地回收旧记录。
需要注意的是,重置操作只影响当前连接的内部记录,不会影响 http2_push 已经配置的静态推送列表,也不会影响磁盘上的缓存文件。重置之后,如果同一个客户端再次请求之前推送过的资源,而该资源仍然在 http2_push 或 Link 头中,Nginx 会重新推送一次。这种重复推送通常不会造成致命问题,因为 HTTP/2 客户端可以拒绝已经缓存的资源,但它会增加少量带宽和 CPU 开销。因此,设置阈值时需要评估重复推送的代价。
如何选择合理的重置阈值
重置阈值的选择直接影响两个指标:连接内存占用和重复推送概率。阈值越小,记录清理越频繁,单连接 diary 占用的内存越低,但资源被重复推送的可能性越高;阈值越大,去重效果越好,但长期活跃的连接可能积累大量记录,增加查找和遍历成本。对于大多数静态内容站点,阈值设置在 200 到 500 之间是一个相对稳妥的范围。
如果站点大量使用 http2_push_preload,并且响应头里通过 Link 声明了较多资源,那么实际推送数量会比仅使用 http2_push 时更多。此时可以先用访问日志或者调试日志统计每个页面实际触发的推送次数,再乘以一个合理的倍数作为阈值。例如页面上有 30 个预加载资源,每个连接平均加载 5 个页面,那么大约会产生 150 次推送记录,阈值可以设为 200,保证一次典型会话不会因为重置而过早清空有效记录。
另一个考虑因素是连接生命周期。移动网络或者弱网环境下,HTTP/2 连接可能频繁中断,连接关闭后 diary 会被销毁,重置阈值的影响较小。但在 WebSocket、长轮询或服务端推送场景中,HTTP/2 连接可能持续数小时,此时合理的重置阈值能有效抑制单连接内存增长。运维上还可以根据 worker 进程的内存曲线来判断:如果发现 Nginx 内存随运行时间缓慢上升,并且主要来自于 HTTP/2 连接,可以尝试将阈值调小,观察内存是否回落。
多 worker 进程与状态验证
Nginx 默认使用多 worker 进程,每个 worker 独立管理自己接受的连接,因此 http2_push_diary_reset 的重置行为也是 worker 级别的。不同 worker 之间不会共享某个连接的 diary 状态,除非使用了额外的共享内存机制。对于大多数单机部署来说,这并不会带来一致性问题,因为 HTTP/2 连接始终由同一个 worker 处理,连接内的记录也是完整保存在该 worker 中。
要验证计数器是否真的被重置,可以打开 Nginx 的 HTTP/2 调试日志。在编译时加入 --with-debug,然后在配置中设置 error_log /var/log/nginx/http2_debug.log debug;。日志中通常会出现类似 push diary reset 或者 counter cleared 的记录,不同版本或第三方模块的输出格式可能不完全相同,但关键点在于能看到计数器归零的动作。若日志中没有明显输出,也可以通过对比重置前后同一连接的行为来判断:记录当前连接的推送次数,达到阈值后再请求一个可推送资源,观察该资源是否被再次推送。
还有一种间接验证方法是监控内存。使用 ps 或者 /proc/PID/status 查看 worker 进程的 RSS,在大量长连接存在时,调小阈值并重载配置,如果内存增量曲线明显放缓,说明重置生效。需要注意的是,Nginx 的 HTTP/2 连接在 reload 后旧 worker 仍可能保留一段时间,验证时要等待旧连接逐步退出,或者使用压力测试工具新建连接观察。
常见误区与注意事项
第一个误区是认为 http2_push_diary_reset 可以直接解决所有 Server Push 内存问题。实际上,它只清理连接内的推送计数记录,并不会影响 Nginx 为 HTTP/2 连接分配的缓冲区、发送队列或请求体临时文件。如果内存压力来自其他模块,例如 proxy_cache、open_file_cache 或大量请求体,重置这个计数器不会有明显效果。因此,需要先通过 stub_status、进程内存统计和日志分析确认内存来源。
第二个误区是阈值设置过小。有些管理员为了追求低内存,把阈值设为 10 甚至 5。这样会导致几乎每个新页面请求都触发一次记录清空,原本已经被推送过的资源再次被推送。客户端虽然会根据缓存状态进行判断,但服务端仍然要执行推送路径、构造响应头并发送数据,带宽和 CPU 开销都会上升。在高并发场景下,这种不必要的重复推送反而会放大性能问题。
最后,如果 http2_push_diary_reset 来自第三方模块或补丁,升级 Nginx 之前需要确认该模块是否兼容目标版本。不同版本中指令参数含义可能发生变化,例如某些版本使用记录条数作为阈值,另一些版本可能以字节数或时间为单位。升级后应重新检查配置文件,避免旧参数被忽略或导致启动失败。
Nginx HTTP/2http2_push_diary_reset计数器重置修改时间:2026-08-20 07:23:52