<-
Apache > HTTP Server > 文档 > 版本 2.4 > 操作指南 / 教程

HTTP/2 指南

可用语言:  en  |  es  |  fr 

这是 Apache httpd 中 HTTP/2 实现的操作指南。此功能已投入生产,您可以预期接口和指令在后续版本中保持一致。

Support Apache!

另请参阅

top

HTTP/2 协议

HTTP/2 是世界上最成功的应用层协议 HTTP 的演进。它专注于更有效地利用网络资源。它不会改变 HTTP 的基本原理,即语义。仍然存在请求和响应以及标头等等。因此,如果您已经了解 HTTP/1,那么您也了解 HTTP/2 的 95%。

关于 HTTP/2 及其工作原理,已经有很多文章。最规范的当然是它的 RFC 7540 (也以更易读的格式提供,YMMV)。因此,您将在其中找到所有细节。

但是,正如 RFC 所做的那样,它并不是真正适合首先阅读的东西。最好先了解什么东西想要做,然后阅读关于如何做它的 RFC。一个更好的入门文档是 http2 explained,由 curl 的作者 Daniel Stenberg 撰写。它也以不断增长的语言列表提供!

太长,没看:阅读本文档时,需要牢记一些新的术语和注意事项。

top

Apache httpd 中的 HTTP/2

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

HTTP/2 的一项提供新功能的特性是 服务器推送。请参阅该部分了解您的 Web 应用程序如何利用它。

top

使用 HTTP/2 支持构建 httpd

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

当您./configure Apache httpd 源代码树时,您需要提供“--enable-http2”作为附加参数来触发模块的构建。如果您的 libnghttp2 位于非寻常位置(无论它在您的操作系统上是什么),您可以使用“--with-nghttp2=<path>”向 configure 宣布其位置。

虽然这对于大多数人来说应该足够了,但有些人可能更喜欢在这个模块中静态链接 nghttp2。对于这些人,存在选项 --enable-nghttp2-staticlib-deps。它的工作原理与将 openssl 静态链接到 mod_ssl 非常类似。

说到 SSL,您需要知道大多数浏览器只会在 https: URL 上使用 HTTP/2,因此您需要一个支持 SSL 的服务器。但不仅如此,您还需要一个支持 ALPN 扩展的 SSL 库。如果您使用 OpenSSL 作为库,则需要至少 1.0.2 版本。

top

基本配置

当您拥有使用 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,除了对 test.example.org 的 SSL 连接,这些连接提供 HTTP/2。

选择一个强大的 SSLCipherSuite

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

提到的协议的顺序也很重要。默认情况下,第一个协议是最优先的协议。当客户端提供多个选择时,最左边的协议将被选中。在

Protocols http/1.1 h2

中最优先的协议是 HTTP/1,它将始终被选中,除非客户端支持 h2。由于我们希望与支持它的客户端进行 HTTP/2 通信,因此更好的顺序是

Protocols h2 h2c http/1.1

还有一件事与排序有关:客户端也有自己的偏好。如果您愿意,您可以配置您的服务器以选择客户端最喜欢的协议

ProtocolsHonorOrder Off

使您编写的协议顺序无关紧要,只有客户端的排序将决定。

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

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

top

MPM 配置

HTTP/2 在所有与 httpd 附带的多处理模块中受支持。但是,如果您使用 prefork mpm,将存在严重限制。

prefork 中,mod_http2 每次连接只会处理一个请求。但客户端(如浏览器)会同时发送多个请求。如果其中一个请求处理时间过长(或是一个长时间轮询请求),其他请求将被阻塞。

mod_http2 默认情况下不会解决此限制。原因是 prefork 今天只在您运行未准备好进行多线程处理的处理引擎时才会选择,例如,如果有多个请求,它将崩溃。

如果您的设置可以处理,配置 event mpm 现在是最好的选择(如果您的平台支持)。

如果您真的被困在 prefork 中,并且想要多个请求,您可以调整 H2MinWorkers 来实现这一点。但是,如果它崩溃了,您将承担所有责任。

top

客户端

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

其他客户端以及服务器都列在 Implementations wiki 上,其中包括 c、c++、common lisp、dart、erlang、haskell、java、nodejs、php、python、perl、ruby、rust、scala 和 swift 的实现。

几个非浏览器客户端实现支持通过明文 h2c 的 HTTP/2。其中最通用的就是 curl

top

用于调试 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 说明

brew install curl --with-openssl --with-nghttp2

对于真正深入的检查,可以使用 wireshark

nghttp2 包还包括客户端,例如

Chrome 通过 特殊的 net-internals 页面 提供其连接的详细 HTTP/2 日志。还有一个有趣的扩展程序,用于 ChromeFirefox,用于可视化您的浏览器何时使用 HTTP/2。

top

服务器推送

HTTP/2 协议允许服务器将客户端从未请求过的响应推送给客户端。对话的语气是:“这里有一个您从未发送过的请求,它的响应很快就会到达……”

但存在限制:客户端可以禁用此功能,并且服务器只能在来自客户端的请求上推送。

目的是允许服务器将客户端很可能需要的资源发送给客户端:客户端请求的 html 页面所属的 css 或 javascript 资源。css 引用的图像集等等。

对客户端来说,这样做的好处是节省了发送请求的时间,这可能从几毫秒到半秒不等,具体取决于双方在地球上的位置。缺点是客户端可能会收到它缓存中已经存在的内容。当然,HTTP/2 允许提前取消此类请求,但仍然会浪费资源。

总结一下:没有一个最佳策略来充分利用 HTTP/2 的这一特性,每个人都在不断尝试。那么,如何在 Apache httpd 中进行尝试呢?

mod_http2 检查响应头中的 Link 头,这些头采用特定格式

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

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

<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 的哈希值),并且不会重复推送相同的资源。连接关闭时,这些信息将被丢弃。

有些人正在考虑如何让客户端告诉服务器它已经拥有哪些内容,以便避免推送这些内容,但这目前还处于高度实验阶段。

另一个在 mod_http2 中实现的实验性草案是 Accept-Push-Policy 头字段,客户端可以在每个请求中定义它接受哪些类型的 PUSH。

PUSH 并不总是能触发预期或希望的请求/响应/性能。网络上有很多关于这个主题的研究,解释了优点和缺点,以及客户端和网络的不同特性如何影响结果。例如:即使服务器推送了资源,也不意味着浏览器会实际使用这些数据。

影响推送响应的主要因素是模拟的请求。PUSH 的请求 URL 由应用程序提供,但请求头来自哪里?例如,PUSH 请求会包含 accept-language 头吗?如果是,其值是什么?

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

所有其他头将被忽略。Cookie 也不会被复制。推送需要 cookie 才能存在的资源将无法正常工作。这可能是一个争论点。但除非与浏览器更明确地讨论这个问题,否则我们应该谨慎行事,不要在 cookie 应该不可见的地方公开它。

top

早期提示

推送资源的另一种方法是在响应准备好之前向客户端发送 Link 头。这利用了 HTTP 的“早期提示”功能,在 RFC 8297 中有描述。

要使用此功能,您需要通过以下方式在服务器上显式启用它:

H2EarlyHints on

(默认情况下它没有启用,因为一些旧的浏览器会遇到此类响应的问题。)

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

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

这将在服务器开始处理请求后立即向客户端发送 "103 Early Hints" 响应。这可能比确定第一个响应头的时间早得多,具体取决于您的 Web 应用程序。

如果 H2Push 已启用,这也会在 103 响应之后立即开始 PUSH。但是,如果 H2Push 已禁用,则仍会向客户端发送 103 响应。

可用语言:  en  |  es  |  fr 

top

评论

注意
这不是问答区。此处发布的评论应针对改进文档或服务器的建议,如果这些建议已被实施或被认为无效/与主题无关,则可能会被我们的版主删除。有关如何管理 Apache HTTP Server 的问题,请咨询我们的 IRC 频道 #httpd(在 Libera.chat 上),或发送到我们的 邮件列表