Go实战--golang中使用echo框架中的HTTP/2、Server Push(labstack/echo、golang.org/x/net/http2)

前端之家收集整理的这篇文章主要介绍了Go实战--golang中使用echo框架中的HTTP/2、Server Push(labstack/echo、golang.org/x/net/http2)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

生命不止,继续 go go go !!!

继续echo web框架,今天搞一下http2。

HTTP2

What is HTTP/2?
HTTP/2 is a replacement for how HTTP is expressed “on the wire.” It is not a ground-up rewrite of the protocol; HTTP methods,status codes and semantics are the same,and it should be possible to use the same APIs as HTTP/1.x (possibly with some small additions) to represent the protocol.

The focus of the protocol is on performance; specifically,end-user perceived latency,network and server resource usage. One major goal is to allow the use of a single connection from browsers to a Web site.

新的二进制格式(Binary Format)
HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。

多路复用(MultiPlexing)
即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。多路复用原理图:

header压缩
HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。

服务端推送(server push)
同SPDY一样,HTTP2.0也具有server push功能

生成证书

go run C:\go\src\crypto\tls\generate_cert.go --host localhost 2017/11/22 10:06:58 written cert.pem 2017/11/22 10 :06:58 written key.pem

echo中的HTTP/2

代码main.go:

package main

import (
    "fmt"
    "net/http"

    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.GET("/request",func(c echo.Context) error {
        req := c.Request()
        format := ` <code> Protocol: %s<br> Host: %s<br> Remote Address: %s<br> Method: %s<br> Path: %s<br> </code> `
        return c.HTML(http.StatusOK,fmt.Sprintf(format,req.Proto,req.Host,req.RemoteAddr,req.Method,req.URL.Path))
    })
    e.Logger.Fatal(e.StartTLS(":1323","cert.pem","key.pem"))
}

浏览器输入:
https://localhost:1323/request

结果:

Protocol: HTTP/2.0
Host: localhost:1323
Remote Address: [::1]:1905
Method: GET
Path: /request

如果出现错误
http: TLS handshake error from [::1]:1735: tls: first record does not look like a TLS handshake.

请检查是否输入的是https

golang.org/x/net/http2

文档地址:
https://godoc.org/golang.org/x/net/http2

获取
get golang.org/x/net/http2

代码main.go:

package main

import (
    "fmt"
    "html"
    "log"
    "net/http"

    "golang.org/x/net/http2"
)

func main() {
    var srv http.Server
    http2.VerboseLogs = true
    srv.Addr = ":8080"
    // This enables http2 support
    http2.ConfigureServer(&srv,nil)

    http.HandleFunc("/",func(w http.ResponseWriter,r *http.Request) {
        fmt.Fprintf(w,"Hi tester %q\n",html.EscapeString(r.URL.Path))
        ShowRequestInfoHandler(w,r)
    })
    // Listen as https ssl server
    // NOTE: WITHOUT SSL IT WONT WORK!!
    log.Fatal(srv.ListenAndServeTLS("cert.pem","key.pem"))
}
func ShowRequestInfoHandler(w http.ResponseWriter,r *http.Request) {
    w.Header().Set("Content-Type","text/plain")
    fmt.Fprintf(w,"Method: %s\n",r.Method)
    fmt.Fprintf(w,"Protocol: %s\n",r.Proto)
    fmt.Fprintf(w,"Host: %s\n",r.Host)
    fmt.Fprintf(w,"RemoteAddr: %s\n",r.RemoteAddr)
    fmt.Fprintf(w,"RequestURI: %q\n",r.RequestURI)
    fmt.Fprintf(w,"URL: %#v\n",r.URL)
    fmt.Fprintf(w,"Body.ContentLength: %d (-1 means unknown)\n",r.ContentLength)
    fmt.Fprintf(w,"Close: %v (relevant for HTTP/1 only)\n",r.Close)
    fmt.Fprintf(w,"TLS: %#v\n",r.TLS)
    fmt.Fprintf(w,"\nHeaders:\n")
    r.Header.Write(w)
}

浏览器输入:
https://localhost:8080/

结果:

Hi tester "/"
Method: GET
Protocol: HTTP/2.0
Host: localhost:8080
RemoteAddr: [::1]:2750
RequestURI: "/"
URL: &url.URL{Scheme:"",Opaque:"",User:(*url.Userinfo)(nil),Host:"",Path:"/",RawPath:"",ForceQuery:false,RawQuery:"",Fragment:""}
Body.ContentLength: 0 (-1 means unknown)
Close: false (relevant for HTTP/1 only)
TLS: &tls.ConnectionState{Version:0x303,HandshakeComplete:true,DidResume:false,CipherSuite:0xc02f,NegotiatedProtocol:"h2",NegotiatedProtocolIsMutual:true,ServerName:"localhost",PeerCertificates:[]*x509.Certificate(nil),VerifiedChains:[][]*x509.Certificate(nil),SignedCertificateTimestamps:[][]uint8(nil),OCSPResponse:[]uint8(nil),TLSUnique:[]uint8{0xa6, 0x3c, 0xfe, 0x93, 0x15, 0x4f, 0x74, 0xfc, 0x97, 0xca, 0x73}}

Headers:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip,deflate,br Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Alexatoolbar-Alx_ns_ph: AlexaToolbar/alx-4.0 Cookie: _ga=GA1.1.981224509.1509938615 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/62.0.3202.94 Safari/537.36

Server Push

Server Push是什么

简单来讲就是当用户的浏览器和服务器在建立链接后,服务器主动将一些资源推送给浏览器并缓存起来,这样当浏览器接下来请求这些资源时就直接从缓存中读取,不会在从服务器上拉了,提升了速率。举一个例子就是:
假如一个页面有3个资源文件index.html,index.css,index.js,当浏览器请求index.html的时候,服务器不仅返回index.html的内容,同时将index.css和index.js的内容push给浏览器,当浏览器下次请求这2两个文件时就可以直接从缓存中读取了。

Server Push原理是什么

要想了解server push原理,首先要理解一些概念。我们知道HTTP2传输的格式并不像HTTP1使用文本来传输,而是启用了二进制帧(Frames)格式来传输,和server push相关的帧主要分成这几种类型:

HEADERS frame(请求返回头帧):这种帧主要携带的http请求头信息,和HTTP1的header类似。

DATA frames(数据帧) :这种帧存放真正的数据content,用来传输。
PUSH_PROMISE frame(推送帧):这种帧是由server端发送给client的帧,用来表示server push的帧,这种帧是实现server push的主要帧类型。

RST_STREAM(取消推送帧):这种帧表示请求关闭帧,简单讲就是当client不想接受某些资源或者接受timeout时会向发送方发送此帧,和PUSH_PROMISE frame一起使用时表示拒绝或者关闭server push。

了解了相关的帧类型,下面就是具体server push的实现过程了:
由多路复用我们可以知道HTTP2中对于同一个域名的请求会使用一条tcp链接而用不同的stream ID来区分各自的请求。
当client使用stream 1请求index.html时,server正常处理index.html的请求,并可以得知index.html页面还将要会请求index.css和index.js。
server使用stream 1发送PUSH_PROMISE frame给client告诉client我这边可以使用stream 2来推送index.js和stream 3来推送index.css资源。
server使用stream 1正常的发送HEADERS frame和DATA frames将index.html的内容返回给client。
client接收到PUSH_PROMISE frame得知stream 2和stream 3来接收推送资源。
server拿到index.css和index.js便会发送HEADERS frame和DATA frames将资源发送给client。
client拿到push的资源后会缓存起来当请求这个资源时会从直接从从缓存中读取。

Golang1.8中的Server Push

代码main.go:

package main

import (
    "fmt"
    "io/IoUtil"
    "net/http"
)

var image []byte

// preparing image
func init() {
    var err error
    image,err = IoUtil.ReadFile("./image.png")
    if err != nil {
        panic(err)
    }
}

// Send HTML and push image
func handlerHtml(w http.ResponseWriter,r *http.Request) {
    pusher,ok := w.(http.Pusher)
    if ok {
        fmt.Println("Push /image")
        pusher.Push("/image",nil)
    }
    w.Header().Add("Content-Type","text/html")
    fmt.Fprintf(w,`<html><body><img src="/image"></body></html>`)
}

// Send image as usual HTTP request
func handlerImage(w http.ResponseWriter,"image/png")
    w.Write(image)
}
func main() {
    http.HandleFunc("/",handlerHtml)
    http.HandleFunc("/image",handlerImage)
    fmt.Println("start http listening :18443")
    err := http.ListenAndServeTLS(":18443","server.crt","server.key",nil)
    fmt.Println(err)
}

浏览器输入:
https://localhost:18443/

可以使用插件HTTP/2 and SPDY indicator
chrome://net-internals/#http2

echo框架中的Server Push

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <Meta charset="UTF-8">
  <Meta name="viewport" content="width=device-width,initial-scale=1.0">
  <Meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>HTTP/2 Server Push</title>
  <link rel="stylesheet" href="/app.css">
  <script src="/app.js"></script>
</head>
<body>
  <img class="echo" src="/echo.png">
  <h2>The following static files are served via HTTP/2 server push</h2>
  <ul>
    <li><code>/app.css</code></li>
    <li><code>/app.js</code></li>
    <li><code>/echo.png</code></li>
  </ul>
</body>
</html>

main.go

package main

import (
    "net/http"

    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    e.Static("/","static")
    e.GET("/",func(c echo.Context) (err error) {
        pusher,ok := c.Response().Writer.(http.Pusher)
        if ok {
            if err = pusher.Push("/app.css",nil); err != nil {
                return
            }
            if err = pusher.Push("/app.js",nil); err != nil {
                return
            }
            if err = pusher.Push("/echo.png",nil); err != nil {
                return
            }
        }
        return c.File("index.html")
    })
    e.Logger.Fatal(e.StartTLS(":1323","key.pem"))
}

浏览器输入:
https://localhost:1323/

参考:
http://www.alloyteam.com/2017/01/http2-server-push-research/

猜你在找的Go相关文章