HTTP2已经被大家所熟知2018年才说自己的站点开始支持HTTP2怕是会误让人以为SpaceX提前十几年实现了殖民火星计划当然至今为止绝大多数支持HTTP2的站点享受的只是HTTP2多路复用特性只有少部分站点享受到了诸如Server Push之类的其他特性

虽然HTTP2规范并没有规定HTTP2必须走TLS甚至刻意区分了走TLSh2和明文传输的h2cHTTP/2 cleartext但是大家几乎把HTTP2h2画上了等号——主流浏览器对h2c置之不理我们又何必去理睬h2c墙倒众人推泛域名证书都免费的年代还不上HTTPS怎么对得起Let's Encrypt

h2c虽然不受浏览器待见但还是有意义的通常用户访问一个流量很大的站点流量首先会打到负载均衡器负载均衡器会将流量转发到某个服务器节点TLS握手和之后的对称加解密都会消耗不少CPU资源为了不给后端服务器额外的压力往往会选择在负载均衡器或者其他反向代理层做TLS termination[1]虽然客户端浏览器和代理层之间使用的是HTTPS协议但是后者与后端服务器之间传输的是明文的HTTP协议

TLS termination

问题在于按照上图做了TLS termination之后HTTP2Server Push就没法用了——后端服务器没法向代理层推送资源因为它们之间使用的往往是不支持Server PushHTTP1协议

为了解决这个问题有些代理层约定使用Link响应头[2]来指定要推送的资源路径比如说如果后端服务器给代理层返回如下响应头

HTTP/1.1 200 OK
Link: </app/style.css>; rel=preload; as=style; nopush
Link: </app/script.js>; rel=preload; as=script

代理层可以理解为后端服务器希望用Server Push推送/app/script.js/app/style.css只作为普通的preload看待从而代理层再次向后端服务器请求/app/script.js将其推送给客户端

这种方案是一种无奈之举Server Push增加了种种局限

要真正解决这一问题可以将代理层与后端服务器之间的协议由HTTP1改为使用h2c这样一来代理层做TLS termination之后可以保持HTTP2协议的原汁原味除了可以做Server Push代理层和后端服务器之间也可以享受HTTP2带来的多路复用从而减少两者之间的连接数

听起来很完美但实际上比较难实现原因在于HTTP2的连接是有状态的比如说streamID一旦变得有状态起来连接池和多路复用就泡汤了要想完全享受多路复用可能需要一些诸如对流ID进行映射之类的额外处理

这篇文章只是分享一些目前的想法可行性还需要在更多夜深人静的时候进行调研和思考


  1. TLS termination proxy: https://en.wikipedia.org/wiki/TLS_termination_proxy ↩︎

  2. Preload: https://www.w3.org/TR/preload/ ↩︎