HTTP/2 guide

这是 Apache httpd 中 HTTP/2 实现的方法指南。此功能可用于生产就绪,并且您可能希望接口和指令保持一致的版本。

HTTP/2 协议

HTTP/2 是世界上最成功的应用程序层协议 HTTP 的演进。它着重于更有效地利用网络资源。它不会改变 HTTP 的基本原理,即语义。仍然有请求,响应,Headers 等等。因此,如果您已经知道 HTTP/1,那么您也知道 95%的 HTTP/2.

关于 HTTP/2 及其工作原理的文章很多。当然,最规范的是其RFC 7540(也提供更易读的格式 YMMV)。因此,您将在其中找到螺母和螺栓。

但是,就像 RFC 一样,首先阅读并不是一件好事。最好先了解某事想要做什么,然后阅读有关该如何完成的 RFC。更好的文档开头是curl的作者 Daniel Stenberg 的http2 explained。它也以越来越多的语言提供!

太长了,没看过:在阅读本文档时,需要牢记一些新术语和陷阱:

Apache httpd 中的 HTTP/2

HTTP/2 协议由其自己的 httpd 模块(恰当地命名为mod_http2)实现。它实现了 RFC 7540 所描述的完整功能集,并支持明文(http :)上的 HTTP/2,以及安全(https :)连接。明文变体的名称为“ h2c”,安全名称为“ h2”。对于h2c,它通过初始 HTTP/1 请求允许* direct *模式和Upgrade:

为 Web 开发人员提供新功能的 HTTP/2 功能之一是Server Push。请参阅该部分以了解您的 Web 应用程序如何使用它。

使用 HTTP/2 支持构建 httpd

mod_http2使用nghttp2库作为其实现基础。为了构建mod_http2,您至少需要在系统上安装libnghttp2的 1.2.1 版本。

当您./configure使用 Apache httpd 源代码树时,需要给它提供'--enable-http2'作为附加参数来触发模块的构建。如果您的libnghttp2居住在不寻常的地方(无论 os 上是什么),您都可以在configure中用'--with-nghttp2=<path>'宣布其位置。

尽管这对于大多数人来说应该可以解决问题,但有些人可能更喜欢此模块中的静态链接nghttp2。对于这些,存在选项--enable-nghttp2-staticlib-deps。它的工作原理与将 openssl 静态链接到mod_ssl的方式非常相似。

说到 SSL,您需要意识到大多数浏览器只会在https: URL 上使用 HTTP/2,因此您需要一台具有 SSL 支持的服务器。不仅如此,您还需要一个支持ALPNextensions 的 SSL 库。如果您使用的是 OpenSSL 库,则至少需要版本 1.0.2.

Basic Configuration

当您使用mod_http2构建httpd时,您需要一些基本配置才能使其生效。与每个 Apache 模块一样,第一件事是您需要加载它:

LoadModule http2_module modules/mod_http2.so

您需要添加到服务器配置的第二个指令是

Protocols h2 http/1.1

这使安全变体 h2 成为服务器连接上的首选协议。要启用所有 HTTP/2 变体时,只需编写:

Protocols h2 h2c http/1.1

根据放置此指令的位置,它会影响所有连接或仅影响到某个虚拟主机的连接。您可以将其嵌套,如下所示:

Protocols http/1.1
<VirtualHost ...>
    ServerName test.example.org
    Protocols h2 http/1.1
</VirtualHost>

这仅允许使用 HTTP/1 进行连接,但与提供 HTTP/2 的test.example.org的 SSL 连接除外。

Choose a strong SSLCipherSuite

SSLCipherSuite需要配置一个强大的 TLS 密码套件。当前版本的mod_http2不会强制执行任何密码,但大多数 Client 端会强制执行。将浏览器指向具有不正确密码套件的启用h2的服务器,将迫使其仅拒绝并退回到 HTTP 1.1. 这是第一次为 HTTP/2 配置 httpd 时常犯的错误,因此请记住这一点,以避免长时间的调试会话!如果要确定要选择的密码套件,请避免使用HTTP/2 TLS 拒绝列表中列出的密码套件。

提到的协议 Sequences 也很重要。默认情况下,第一个是最喜欢的协议。当 Client 提供多种选择时,将选择最左侧的一个。在

Protocols http/1.1 h2

最优选的协议是 HTTP/1,除非 Client 端支持 h2,否则它将始终被选择。由于我们想与支持 HTTP/2 的 Client 端对话,因此更好的 Sequences 是

Protocols h2 h2c http/1.1

Order 还有另外一件事:Client 也有自己的偏好。如果需要,可以将服务器配置为选择 Client 端最喜欢的协议:

ProtocolsHonorOrder Off

使 *所写的协议无关紧要,只有 Client 的订单才能决定。

最后一件事:不检查您配置的协议的正确性或拼写。您可以提及不存在的协议,因此无需通过任何<IfModule>检查来保护Protocols

有关配置的更多高级技巧,请参见关于尺寸的模块部分如何使用同一证书 Management 多个主机

MPM Configuration

httpd 随附的所有 multiprocessing 模块均支持 HTTP/2.但是,如果您使用preforkmpm,则会有严格的限制。

prefork中,mod_http2每次连接一次只能处理一个请求。但是 Client 端(例如浏览器)将同时发送许多请求。如果其中之一需要花费很长时间进行处理(或者是长时间轮询),则其他请求将停止。

mod_http2默认情况下将无法解决此限制。原因是,如果您运行的处理器未准备好用于多线程,例如,今天仅选择prefork。会因多个请求而崩溃。

如果您的设置可以解决问题,那么配置eventmpm 是当今最好的选择(如果您的平台支持)。

如果您确实对prefork感到困惑,并且想要多个请求,则可以调整H2MinWorkers以使其成为可能。但是,如果破裂,您将拥有这两个部分。

Clients

几乎所有现代浏览器都支持 HTTP/2,但仅通过 SSL 连接:Firefox(v43),Chrome(v45),Safari(自 v9),iOS Safari(v9),Opera(v35),Chrome for Android(v49)和 Internet 资源 Management 器(Windows10 上为 v11)(source)。

列出了其他 Client 端以及服务器在实现 Wiki 上,其中包括 c,c,common lisp,dart,erlang,haskell,java,nodejs,php,python,perl,ruby,rust,scala 和 swift 的实现。

一些非浏览器 Client 端实现通过明文 h2c 支持 HTTP/2.最通用的是curl

调试 HTTP/2 的有用工具

当然要提到的第一个工具是curl。请确保您的版本支持 HTTP/2,并检查其Features

$ curl -V
    curl 7.45.0 (x86_64-apple-darwin15.0.0) libcurl/7.45.0 OpenSSL/1.0.2d zlib/1.2.8 nghttp2/1.3.4
    Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 [...] 
    Features: IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2

Mac OS homebrew notes

brew 安装 curl --with-openssl --with-nghttp2

并进行深入的检查wireshark

nghttp2软件包还包括 Client 端,例如:

Chrome 浏览器通过网络内部特别页面提供详细的 HTTP/2 日志。 ChromeFirefox还有一个有趣的扩展,可以在浏览器使用 HTTP/2 时可视化。

Server Push

HTTP/2 协议允许服务器向从未要求的 Client 端推送响应。对话的基调是:“这里有一个您从未发送过的请求,对此的响应将很快到达……”

但是有一些限制:Client 端可以禁用此功能,并且服务器只能对来自 Client 端的请求进行推送。

目的是允许服务器向最可能需要的 Client 端发送资源:属于 Client 端请求的 html 页面的 css 或 javascript 资源。 css 等引用的一组图像。

Client 端的优势在于,它节省了发送请求的时间,该时间可能从几毫秒到半秒不等,具体取决于两者的位置。缺点是 Client 端可能会收到其缓存中已经拥有的东西。当然,HTTP/2 允许尽早取消此类请求,但是仍然浪费了资源。

总结一下:关于如何最好地利用 HTTP/2 的这一功能,没有一个好的策略,并且每个人都还在进行实验。那么,如何在 Apache httpd 中进行实验呢?

mod_http2以某种格式检查LinkHeaders 的响应 Headers:

Link </xxx.css>;rel=preload, </xxx.js>; rel=preload

如果连接支持 PUSH,则这两个资源将发送到 Client 端。作为 Web 开发人员,您可以直接在应用程序响应中设置这些 Headers,也可以通过以下方式配置服务器

<Location /xxx.html>
    Header add Link "</xxx.css>;rel=preload"
    Header add Link "</xxx.js>;rel=preload"
</Location>

如果要使用preload链接而不触发 PUSH,则可以使用nopush参数,如下所示:

Link </xxx.css>;rel=preload;nopush

或者您可以完全使用指令为服务器禁用 PUSH

H2Push Off

还有更多:

该模块将记录每个连接已推送的日志(基本上是 URL 哈希),并且不会将相同的资源推送两次。连接关闭时,该信息将被丢弃。

有人在考虑 Client 端如何告诉服务器已经拥有的服务器,因此可以避免对这些服务器进行推销,但是现在这都是高度试验性的。

mod_http2中已实现的另一项实验性草案是接受-推-Policy 标题字段,Client 可以针对每个请求定义接受哪种类型的 PUSH。

PUSH 可能并不总是触发人们期望或希望的请求/响应/性能。在网上可以找到有关此主题的各种研究,这些研究解释了优点和缺点以及 Client 端和网络的不同功能如何影响结果。例如:仅仅因为服务器推送资源并不意味着浏览器将实际使用数据。

影响被按下的响应的主要因素是模拟的请求。 PUSH 的请求 URL 由应用程序提供,但是请求 Headers 来自何处?例如,PUSH 会请求accept-languageHeaders 吗?如果是,应使用什么值?

Apache 将查看原始请求(触发 PUSH 的请求),并将以下 Headers 复制到 PUSH 请求:user-agentacceptaccept-encodingaccept-languagecache-control

所有其他标题都将被忽略。 Cookies 也不会被复制。推送需要显示 cookie 的资源将不起作用。这可能是一个辩论的问题。但是,除非使用浏览器对此进行更清楚的讨论,否则请谨慎行事,不要在通常不可见的 Cookie 上公开它们。

Early Hints

推送资源的另一种方法是在响应尚未准备好之前就向 Client 端发送LinkHeaders。这使用称为“早期提示”的 HTTP 功能,并在RFC 8297中进行了描述。

为了使用它,您需要通过以下方式在服务器上显式启用它

H2EarlyHints on

(由于某些较旧的浏览器因此类响应而跳闸,因此默认情况下未启用该功能.)

如果启用此功能,则可以使用指令H2PushResource来触发早期提示和资源推送:

<Location /xxx.html>
    H2PushResource /xxx.css
    H2PushResource /xxx.js
</Location>

服务器开始处理请求后,这将向 Client 端发送"103 Early Hints"响应。根据您的 Web 应用程序,这可能比确定第一个响应 Headers 的时间早得多。

如果启用了H2Push,则也将在 103 响应后立即开始 PUSH。但是,如果禁用H2Push,则会将 103 响应发送给 Client 端。

首页