golang在http/2这块做的比较早但是因为历史原因导致API比较令人迷惑网上很多同学在抱怨。
我这里记录一下如何正确的实施HTTP/2的客户端与服务端。
HTTP/2协议
HTTP/2协议握手分2种方式一种叫h2一种叫h2c。
h2要求必须使用TLS加密在TLS握手期间会顺带完成HTTPS/2协议的协商如果协商失败比如客户端不支持或者服务端不支持则会使用HTTPS/1继续后续通讯。
h2c不使用TLS而是多了一次基于HTTP协议的握手往返来完成向HTTP/2协议的升级一般不建议使用。
GO标准库
GO的http库默认支持HTTP/2协议只要我们使用TLS则会默认启动HTTP/2特性。
http库在设计API时并没有支持用户使用h2c而是鼓励使用h2。
只要我们使用TLS则http库就会默认进行HTTPS/2协商协商失败则蜕化为HTTPS/1。
让很多开发者迷惑的点在于当我们希望对http Client或者Server做一些更加定制化的配置时就会覆盖掉http库的默认行为从而导致无法启用HTTP/2协议。
下面我就会告诉大家到底怎么保证HTTP/2协议的启用。
服务端
我们明确一定要用h2模式也就是牺牲TLS加密的时间但是换来的就是HTTP/2的使用便捷性。
制作证书
我们首先需要为服务端制作证书和私钥其中证书会在收到请求时发回给客户端。
证书是我们自签的没有第三方CA作验证所以客户端需要关闭校验证书有效性的特性。
1生成服务端私钥 openssl genrsa -out default.key 2048 2生成服务端证书 openssl req -new -x509 -key default.key -out default.pem -days 3650
default.key是私钥default.pem是证书。
校验证书
因为我们是X509格式签名的证书所以程序做好先做一下有效性校验
// TLS证书解析验证 if _, err = tls.LoadX509KeyPair(G_config.ServerPem, G_config.ServerKey); err != nil { return common.ERR_CERT_INVALID }
确认证书有效后我们最终通过serverTLS传入证书和私钥启动一个HTTPS/2服务
// HTTP/2 TLS服务 server = &http.Server{ ReadTimeout: time.Duration(G_config.ServiceReadTimeout) * time.Millisecond, WriteTimeout: time.Duration(G_config.ServiceWriteTimeout) * time.Millisecond, Handler: mux, } // 监听端口 if listener, err = net.Listen("tcp", ":" + strconv.Itoa(G_config.ServicePort)); err != nil { return } // 拉起服务 go server.ServeTLS(listener, G_config.ServerPem, G_config.ServerKey)
除了使用ServeTLS来启动支持HTTPS/2特性的服务端之外还可以通过http2.ConfigureServer来为http.Server启动HTTPS/2特性并直接使用Serve来启动服务。
客户端
客户端最重要的是配置Transport所谓Transport就是底层的连接管理器包括了协议的处理能力。
因为我们有很多定制化Client配置的需求所以我们自己生成了一个Transport而不是内置的Transport
transport = &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true,}, // 不校验服务端证书 MaxIdleConns: G_config.GatewayMaxConnection, MaxIdleConnsPerHost: G_config.GatewayMaxConnection, IdleConnTimeout: time.Duration(G_config.GatewayIdleTimeout) * time.Second, // 连接空闲超时 }
其中TLS配置声明了InsecureSkipVerify=true表示不向CA校验证书的有效性。
类似于服务端因为我们没有使用内置的Client Transport所以我们需要使用http2.ConfigureTransport来启动HTTPS/2特性
// 启动HTTP/2协议 http2.ConfigureTransport(transport)
最后将Transport配置给Client负责底层的连接与协议管理
// HTTP/2 客户端 gateConn.client = &http.Client{ Transport: transport, Timeout: time.Duration(G_config.GatewayTimeout) * time.Millisecond, // 请求超时 }
最后
首先理解h2和h2c的区别然后明确Go语言推荐使用h2。
最后当我们没有使用默认的http配置时我们需要通过http2.ConfigureXXX重新配置启用HTTP2/S特性。