登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go net/http Push 已不支持时如何规划资源加载

来源:17golang原创

时间:2026-09-15 13:19:15 484浏览 收藏

如果你的 Go 服务里还把 http.Pusher.Push 当成 CSS、脚本必须提前到达的条件,迁移时最容易踩的坑就是:客户端禁用 Push 后,页面也跟着少资源或直接报错。更稳妥的做法是把 Push 降级为可选加速,主路径交给 HTML 的 preload,需要更早提示时再加 HTTP 103 Early Hints。

要点速览
  • Go 的 Pusher 接口仍可做能力探测,但 Push 返回 http.ErrNotSupported 是正常分支。
  • 只给首屏关键 CSS、字体或模块脚本做预加载,非关键资源继续按页面发现或懒加载。
  • 把“页面能否正确返回”和“资源是否提前到达”分开,才能兼容不同协议、客户端和缓存状态。

先把 Push 从必选依赖改成能力探测

Go 标准库没有把 http.PusherResponseWriter 体系中删掉。它代表当前响应写入器可能支持 HTTP/2 Server Push;真正调用时,客户端关闭 Push 或底层连接不支持,就会得到 http.ErrNotSupported。所以标题里的“已不支持”应理解为运行环境中经常不可用,而不是 Go 编译器一定拒绝这段代码。

先做这层判断:Push 成功只是提前发送资源的优化,失败仍要继续生成完整 HTML。不要把 Push 错误包装成 500,也不要用是否实现接口来决定页面是否输出

先给关键资源做预算,再决定提示方式

资源规划先回答“首屏在什么时候需要它”。通常可以优先考虑阻塞首屏样式的 CSS、首屏确实要执行的模块脚本,以及影响首屏文字显示的字体;统计脚本、折叠区图片和交互尚未触发的模块不应因为迁移 Push 就全部提前加载。

资源默认策略判断点
首屏 CSSHTML preload 或 103 提示是否马上参与渲染
关键模块脚本preload 配合正常 scriptas 与实际用途一致
分析脚本、折叠图片正常发现或延后加载提前下载是否浪费带宽
首屏关键资源与延后资源的加载预算示意图
图2:关键资源与延后资源的预算示意图,强调 preload 也需要节制。

把 Push 变成可选加速层

下面的处理顺序是“尝试 Push、发送可替代的 Link 提示、最后返回页面”。示例只展示服务端策略,图中的箭头是操作示意,不代表本机真实抓包结果。

package main

import (
    "errors"
    "io"
    "log"
    "net/http"
)

func page(w http.ResponseWriter, r *http.Request) {
    // 中文注释:Push 只是加速层,不能决定页面是否成功。
    if p, ok := w.(http.Pusher); ok {
        if err := p.Push("/static/app.css", nil); err != nil && !errors.Is(err, http.ErrNotSupported) {
            // 中文注释:记录真正异常;能力不足属于可预期回退。
            log.Printf("optional http push failed: %v", err)
        }
    }

    // 中文注释:让支持 Early Hints 或 Link preload 的客户端自行决定是否提前取 CSS。
    w.Header().Add("Link", "/app.css>; rel=preload; as=style")
    w.Header().Set("Content-Type", "text/html; charset=utf-8")
    io.WriteString(w, `
页面内容
`) } func earlyHint(w http.ResponseWriter) { // 中文注释:103 只是提示,随后仍需发送最终的 200 响应。 w.Header().Add("Link", "/app.css>; rel=preload; as=style") w.WriteHeader(http.StatusEarlyHints) }

实际项目里不必同时叠加所有提示。若网关已经负责 103,应用层可只保留 HTML preload;若链路不稳定,至少保证最终 HTML 自带可执行的 stylesheet 或 script 标签。Link 中的 as 要和资源类型一致,避免同一文件被浏览器以不同用途重复取回。

Go net/http Push 不可用时回退到 Early Hints 和 preload 的关系示意图
图1:Push、Early Hints 与 preload 的分层回退关系示意图;Push 失败不应阻断主页面。

反例是把资源提前发送当成正确性的证明

最危险的迁移写法是:只有 Push 返回 nil 才输出资源,或者收到 ErrNotSupported 就直接结束 handler。这样在禁用 Push 的浏览器、HTTP/1.1 连接、缓存已经命中的客户端上都会出现不一致结果。

另一个反例是把几十个 JS、字体和图片全部标记为 preload。Early Hints 和 preload 都只是“建议更早取”,并不会替你判断缓存,也不会消除带宽竞争。越早发不等于越快,关键是减少首屏等待,而不是扩大并发下载。

用请求链和缓存结果判断方案是否值得保留

回归时至少覆盖支持 HTTP/2 的连接、明确禁用 Push 的客户端、HTTP/1.1 和已有缓存四种情况。检查点不是“有没有 PUSH_PROMISE”,而是最终 HTML 是否完整、关键 CSS 是否只下载一次、非关键资源是否没有挤占首屏带宽。再结合首屏渲染、最大内容绘制和样式阻塞时间观察,才能决定保留 103、HTML preload,还是完全依赖浏览器正常发现。

架构判断可以压缩成一句话:页面正确性放在最终响应,资源优先级放在 preload 或 Early Hints,Push 只作为连接具备能力时的额外收益。

常见问题

Go 现在还能写 http.Pusher 吗?

可以。它仍是 net/http 的能力接口,但具体连接可能不支持,调用者必须处理 http.ErrNotSupported

Push 失败后需要重试吗?

通常不需要在同一个响应里盲目重试。转向最终 HTML 的 preload 或由网关发送 103,并记录必要的能力与性能指标。

Link preload 能完全替代 Push 吗?

它们都能帮助资源更早被发现,但控制权不同。preload 让客户端按缓存和优先级决定取用,通常更适合作为稳定主路径;是否加入 103 还要看代理链路和实际收益。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>