美文网首页程序员Golang语言社区
HTTP2服务器推送的第一次尝试

HTTP2服务器推送的第一次尝试

作者: Gopher指北 | 来源:发表于2020-10-19 09:52 被阅读0次

    来自公#众#号:新世界杂货铺

    在HTTP1.x中,访问一个页面,浏览器首先获取HTML资源,然后在解析页面时增量地获取其他资源,服务器必须等待浏览器发出请求后才下发页面内资源。而服务器实际上是知道页面内资源有哪些的,如果服务器能够在浏览器显式请求资源之前就将资源推送到浏览器,页面加载速度将会大大提示,这也是本篇的主旨。

    本篇主要分为两个部分,第一部分是用go实现的服务器推送例子,第二部分是自签名证书。为什么会有自签名证书,这里笔者先卖个关子,继续阅读后文将会守得云开见月明。

    服务器推送例子

    目前仅有HTTP2支持服务器推送,HTTP1.x不支持服务器推送,那我们在代码中应该如何判断当前服务器是否支持推送?

    在Go中,我们通过判断http.ResponseWriter是否实现了http.Pusher接口就可以知道当前服务器是否支持推送。

    下面为笔者写下的第一个服务器推送例子:

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        if r.URL.Path != "/" {
            http.NotFound(w, r)
            return
        }
        pusher, ok := w.(http.Pusher)
        if ok {
            // 主动推送服务资源
            if err := pusher.Push("/static/app.js", nil); err != nil {
                log.Printf("Failed to push: %v", err)
            }
            if err := pusher.Push("/static/style.css", nil); err != nil {
                log.Printf("Failed to push: %v", err)
            }
        }
        // 下发浏览器首屏资源
        fmt.Fprintf(w, `<html>
        <head>
            <title>新世界杂货铺</title>
            <link rel="stylesheet" href="/static/style.css"">
        </head>
        <body>
            <div>Hello 新世界杂货铺</div>
            <div id="content"></div>
            <script src="/static/app.js"></script>
        </body>
        </html>`)
    })
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
    http.ListenAndServe(":8080", nil)
    

    上述代码中app.js内容如下:

    document.getElementById("content").innerHTML = '"新世界杂货铺" from js'
    

    运行上述代码后,在浏览器中访问http://localhost:8080/得到如下结果:

    image

    我们看上图中标红部分发现js资源和样式资源并不是服务器推送下发的,而且使用的是HTTP1.1,结合笔者前面的文章知道Go是支持HTTP2的且HTTP2需要使用SSL/TLS即HTTPS。因此,笔者修改监听代码如下:

    http.ListenAndServeTLS(":8080", "ca.crt", "ca.key", nil)
    

    再次运行上述代码,并在浏览器中访问https://localhost:8080/得到如下结果:

    image

    我们看图中红色部分知本次请求使用了HTTP2协议,并且静态资源由服务器推送。

    上述代码中ca.crtca.key分别为自签名证书以及私钥,该证书及私钥已上传至笔者的github,github链接见文末。

    生成自签名证书

    注:笔者生成证书环境为macOS

    笔者生成自签名证书时,先祭出搜索大法,然后使用网上的命令生成证书,最后证书是生成了,但是运行上述例子后在浏览器中的访问结果却不尽人意。

    首先执行下述命令生成证书:

    // 生成私钥
    openssl genrsa -out old.key 2048
    // 生成证书签名请求,此时需要填写一些证书信息
    openssl req -new -key old.key -out old.csr
    // 签发证书
    openssl x509 -req -days 365 -in old.csr -signkey old.key -out old.crt
    

    修改例子中的证书和私钥为old.crtold.key,最后在Chrome浏览器中访问结果如下:

    image

    上述页面给了不安全提示却无法继续不安全访问(Safari浏览器可以进行不安全地访问),作为一个轻微强迫症患者,笔者的内心有一丝丝难受。

    于是笔者翻了openssl官网,最后对生成自签名证书的命令做了改进。

    首先在执行命令的目录创建一个ca.cnf(可以随意命名),并在该文件中写入如下内容:

    # 更多x509v3_config见https://www.openssl.org/docs/man1.1.1/man5/x509v3_config.html
    [ ca_conf ]
    extendedKeyUsage = serverAuth # 能够继续进行不安全地访问的关键
    basicConstraints = CA:FALSE
    

    改进后的签名命令如下:

    // 生成私钥
    openssl genrsa -out ca.key 2048
    // 生成证书签名请求,此时需要填写一些证书信息
    openssl req -new -key ca.key -out ca.csr
    // 签发证书
    openssl x509 -req -sha256 -extfile ca.cnf -extensions ca_conf -days 365 -in ca.csr -signkey ca.key -out ca.crt
    

    此证书即为前面例子所使用的证书,并且第一次在浏览器中访问会有如下提示:

    image

    点击图中红框部分即可正常访问页面。

    什么时候使用推送

    完成上面的例子之后,笔者特意去观察了百度、淘宝和谷歌的首页发现大家均已开始使用HTTP2,但是好像还没有公司使用服务器推送。

    因此笔者下面的总结全凭个人经验猜测:

    1. 服务器推送不要滥用,仅推送影响该页面展示的关键资源,毕竟前端的懒加载已经十分成熟。
    2. 浏览器能够对资源进行缓存,对于已经缓存了的资源继续推送没有意义,所以这种场景下要避免二次推送。

    最后,衷心希望本文能够对各位读者有一定的帮助。

    参考

    https://blog.golang.org/h2push

    https://blog.csdn.net/qq_41874930/article/details/107795556

    注:

    1. 写本文时, 笔者所用go版本为: go1.15.2
    2. 文章中所用完整例子:https://github.com/Isites/go-coder/blob/master/http2/server-push/main.go

    相关文章

      网友评论

        本文标题:HTTP2服务器推送的第一次尝试

        本文链接:https://www.haomeiwen.com/subject/naecmktx.html