golang h2c 实践

二维码
| May 19, 2020 | 原创

h2ch2 的非TLS版本,即既希望使用 http2 的新特性,如压缩 header 头,多路复用等等新特性,又不想使用 tls 证书的版本。

h2c 目前浏览器都没有支持的计划,毕竟在浏览器侧,使用未加密的 http 请求,感觉像是历史的倒退。但在某些场景下,h2c 还是有他的用武之地的,比如服务端侧(server side)。

当服务端存在多个相互调用的服务模块时,我们除了选择使用 rpc 调用外,另一外选择即是基于 http 协议 Restful 接口。如果我们又想使用 h2 的一些新特性,那么 h2c 这时候就能排上用场。

获取你会问,那为啥不直接使用 h2 呢?答案毋庸质疑,架在 https 上的接口同比 http 接口,性能上差的不是一丁半点,但都是作为服务端的程序,安全因素我们可以采用其他手段来保证,接口服务之间的通信我们,我们希望更快,更省cpu资源,那这时候 h2c 无疑比 h2 更合适一些。

golang 中使用 h2c 非常简单,官方提供的了专门的 h2c 包供我们使用:

import (
	"fmt"
	"golang.org/x/net/http2"
	"golang.org/x/net/http2/h2c"
	"net/http"
	"time"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
        _, _ = fmt.Fprint(w, "test request")
    })

    // http1
    s := &http.Server{
        Addr:             "0.0.0.0:8080",
	Handler:           h2c.NewHandler(mux, &http2.Server{}),
	ReadTimeout:       time.Second * 5,
	WriteTimeout:      time.Second * 5,
    }

    s.ListenAndServe()
}

官方推荐的写法是基于 http 的渐进增强支持 h2c 的功能(如上代码),即如果发觉请求不支持 h2c 功能,那么会降级使用 http1 协议,这样能保证服务的可用性。

那如何测试 h2c 协议呢,我们这里用到了另一个专门 h2 测试工具:nghttp2 ,如果我们需要发送一个 h2c 的请求,直接使用 -u 参数即可:

nghttp http://localhost:8080/test -u -v

如果当你发觉输出中存在如下字样,则表示使用 h2c 协议交互成功:

➜  ~ nghttp http://localhost:8080/test -u -v
[  0.003] Connected
[  0.003] HTTP Upgrade request
GET /test HTTP/1.1
host: localhost:8080
connection: Upgrade, HTTP2-Settings
upgrade: h2c
...

性能测试

下面我们对比测试一下,httphttp2h2c 的性能数据,同样我们还是使用 nghttp2 提供给我们的另一个压测工具:h2load