LAMP 启用 HTTP/2 一波三折

最近在优化博客的访问速度(最终也会总结成文的,无意中又双叒叕立了个 Flag,无视就好),针对静态资源优化还是停留在 HTTP/1.1 时代的老思路:无非是静态资源合并压缩、启用 CDN、雪碧图、静态资源多域名加载,巴拉巴拉~~最近受到 Jerry Qu 的启发,HTTP/2 对前端页面加载有着巨大提升,前端界也在关注,我也得赶趟启用上,反正现在用上了 VPS,随便我折腾!既然是折腾免不了踩坑,特别是对于我这只抠脚菜鸡前端,运维白痴来说。

为什么 HTTP/2 更快

别说话,点我:https://http2.akamai.com/demo 先来感受下 HTTP/2 的速度优势。感受完了接下来可以看我的絮叨了,这块我会说得很多,首先满足于增强自我对 HTTP/2 的认知,其次你爱看不看。如果看不懂不要紧,跳过即可,你只需知道 HTTP/2 快就好了。

多路复用

这得了聊回 HTTP/1.1 时代:浏览器在同一时间内,会限制同一个域的同时请求数,而 HTTP/2 无此限制。

再往前深究,HTTP 协议是建立在 TCP 协议之上的一种应用,HTTP 链接本质也是 TCP 连接。大家都知道 TCP 链接需要三次握手,四次挥手开销是很大的。你说是不是,握来握去累不累,当然 TCP 链接这么做是为了满足"在不可靠信道上可靠地传输信息"这一目标。HTTP/1.0 中如果不在请求头设置 Connection: Keep-Alive,那么每次请求完成就会断开,新的请求需重新建立 HTTP 连接。

HTTP/1.1 中边默认支持了持久链接,省去了频繁的建立关闭 TCP 链接的开销。但是一个 TCP 的连接在同一时间内只能允许一个请求经过,后续请求得等上一个完成才行,是串行的。那么一个 TCP 连接不行,那就多来几个不就快了吗,但是来一打服务器也吃不消啊,于是乎浏览器为了照顾服务器,限制了同一域名下允许的并发请求资源数(有木有感觉好暖😊)。开发者一看,既然里浏览器限制了一个域名下的并发请求资源数,我多来几个域名就好了嘛,结果这么一来,又多个 N + 1 个 TCP 链接的开销处理(浏览器与服务器内心 OS: MMP!)。

HTTP/2 里面的请求只通过一个 TCP 链接建立,后续请求直接通过 Stream 的方式传输。每个 Stream 都是双向传输的且有一个 Stream ID。Stream 是由 Frame 组成的,HTTP/2 传输数据直接以二进制格式形式,其很容易被处理成一个个 Frame, Frame Header 都有一个 Stream ID。好比 TCP 链接上的数据包,通过 IP:PORT 来区分出数据包去往哪里一样,有了 Stream ID,然后就可以在一条 TCP 链路上多路快车道欢脱的传输请求和响应数据了,且是并行传输。谁先来后来也不重要,因为有 Stream ID 作为表示可以重组成正确的数据。

Stream 并发也会有优先级和依赖关系,优先级高的 Stream 会被优先发送,以确保重要的东西被优先加载,比如图片的优先级就低于 JavaScript 和 Style。

作为一个计算机网络基础当初就没好好学更何谈还给老师的计科生来说,以上解释源自本人理解与趣味演绎,如果错误还请斧正。

HTTP 头部压缩

一个 HTTP 请求必然需发送一 HTTP 头部,即便是发送 Hello,World!这么点字节,这种极端的情况下 HTTP 头部远大于发送的内容,其实实际场景中这种情况还不少。如今 Web 页面产生的请求数越来越多,也会请求很多的时候 HTTP 头部,也算是一笔不小的开销。HTTP/1.1 中头部是以 plain text 传输的,且有很多数据是重复的亦或变动不频繁的,比如 UserAgent、Cookie 之类。HTTP/2 中引入了头部压缩,能够节约传输头部的网络开销,毕竟 HTTP/1.1 中消息主体都可以 zip 压缩了。

Server Push

比如客户端请求一个资源 A ,服务器知道也可能需要资源 B 的情况下,服务器可以在客户端发送请求前,主动将资源 B 推送给客户端,这样资源 B 就可以提前缓存在客户端了,这很类似 preload

HTTP/2 中在 HTPP 头部添加如下信息可实现服务器推送。

Link: </css/styles.css>; rel=preload; as=style, </js/scripts.js>; rel=preload; as=script, </img/logo.png>; rel=preload; as=image

Apache 中可如下配置:

<FilesMatch "\.html$">
    Header add Link "</css/styles.css>; rel=preload; as=style"
    Header add Link "</js/scripts.js>; rel=preload; as=script"
    Header add Link "</img/logo.png>; rel=preload; as=image"
<FilesMatch>

唠叨完为什么,接下来说怎么做?

如何在 Apache 上开启 HTTP/2

Apache 2.4.17 开始内置 HTTP/2 协议 。而我的 Ubuntu 16.04 LTS 上在安装版本是 2.4.18,这样也就满足了最低版本要求。在 Apache2 上开启 HTTP/2 只需启用 HTTP/2 模块即可。 于是乎,不出意料的报错了,没看到标题中的一波三折么!!!

sudo a2enmod http2
sudo service apache2 restart
# ERROR: Module http2 does not exist!

这是第一折!

一番查阅,原来是 Ubuntu 16.04 是一个 LTS 版本,要稳定为主,而 mod_http2 是试验性的,所以依旧不予加。
via 在 Ubuntu 上 开启 Apache2 HTTP/2 协议

大神 ondrej 则提供了最新的 Apache Httpd(apache2)和 mod_http2 的二进制包还有 nghttp2 的 PPA 源支持。另外 OpenSSL 版本需大于等于 1.0.2,幸亏 Ubuntu 16.04 自带 1.0.2g 版本,无需再折腾,不过你也可以索性一起装了。

add-apt-repository ppa:ondrej/apache2
apt-get update
apt-get install apache OpenSSL

openssl version
# OpenSSL 1.0.2g  1 Mar 2016
apache2 -v
# Server version: Apache/2.4.33 (Ubuntu)
# Server built:   2018-03-27T10:59:34

然后捏,你就需要在 Apache 中启用相关配置了,先编辑虚拟机配置文件。

vim /etc/apache2/sites-enabled/000-default.conf 

然后加入如下配置,当时我自己并不知道 443 端口是干嘛的,所以没加。

# 80 端口
<VirtualHost *:80>
    # ...
    ProtocolsHonorOrder On
    Protocols h2c http/1.1
    #...
</VirtualHost>

# 443 端口
<VirtualHost *:443>
    # ...
    ProtocolsHonorOrder On
    Protocols h2 h2c http/1.1
    #...
</VirtualHost>

保存,退出,最后满怀希望的重启,刷新页面。

sudo service apache2 restart

打开 Chrome DevTools > Network > 勾选上 Protocol,可并没有看到可爱的 h2 标示,还是 http/1.1,为神马?!!!因为目前没有浏览器支持 80 端口的 HTTP/2…要想浏览器支持 HTTP/2 只能启用 HTTPS。

超文本传输安全协定(英语:Hypertext Transfer Protocol Secure,缩写:HTTPS,常称为HTTP over TLS,HTTP over SSL或HTTP Secure)是一种透过计算机网路进行安全通讯的传输协议。HTTPS经由HTTP进行通讯,但利用SSL/TLS来加密封包。HTTPS开发的主要目的,是提供对网站伺服器的身份认证,保护交换资料的隐私与完整性。这个协议由网景公司(Netscape)在1994年首次提出,随后扩展到网际网路上。
via Wikipedia

这是第二折,接下来我们需要启用 HTTPS,而我们需要两步走

申请 SSL 证书

SSL 证书分为三种类型:

  • 域名型 SSL 证书(DV SSL)
  • 企业型 SSL 证书(OVSSL)
  • 增强型 SSL 证书(EVSSL)

作为穷人家的孩子,我们这里只说如何申请免费的 SSL DV 证书:

  1. Let's Encrypt:免费,快捷,支持多域名(不是通配符)。缺点暂时只有三个月有效期,到期需续签。
  2. MySSL:一年有效期
  3. 沃通:一年有效期

我懒这里有很多一键生成 SSL 证书的方式,操作方式非常简单,也不再赘述。

  1. 腾讯云:https://console.qcloud.com/ssl
  2. 七牛:https://www.qiniu.com/ssl
  3. 又拍云:https://www.upyun.com/https
  4. 阿里云:https://www.aliyun.com/product/cas

选择 Apache 导出得到如下文件,

bluest.me.cer
bluest.me.key
bluest.me_ca.crt

部署 SSL 证书

将上述文件上传到 /usr/local/ssl/ 下(文件路径可以改变),配置 Apache 配置文件:

vim /etc/apache2/sites-enabled/000-default.conf 

将如下代码添加到 443 端口配置中去:

<VirtualHost *:443>
    #...
    ServerName bluest.me
    SSLEngine on
    SSLCertificateFile /usr/local/ssl/bluest.me.cer
    SSLCertificateKeyFile /usr/local/ssl/bluest.me.key
    SSLCACertificateFile /usr/local/ssl/bluest.me_ca.crt
</VirtualHost>

保存,退出,最后满怀希望的重启,刷新页面。

sudo apache2ctl configtest
sudo service apache2 restart

于是页面有了可爱的小绿标,打开 Chrome DevTools > Network > 勾选上 Protocol,又木有看到 h2 标示,还是 http/1.1,为神马我要说又,为神马?!!!

Apache MPM module

第三个折到了,再次查阅资料得知:

AH10034: The mpm module (prefork.c) is not supported by mod_http2. The mpm determines how things are processed in your server. HTTP/2 has more demands in this regard and the currently selected mpm will just not do. This is an advisory warning. Your server will continue to work, but the HTTP/2 protocol will be inactive.
via: https://http2.pro/doc/Apache#prefork-http2

Apache 有三种 MPM:具体三种 MPM 有啥区别不是本文重点,有兴趣需自己了解:

  1. prefork MPM
  2. worker MPM
  3. event MPM

上述资料说人话就是 mod_http2 勾选上不支持 prefork MPM 支持 event MPM,好那就换!

a2dismod mpm_prefork
a2enmod mpm_event
sudo service apache2 restart

结果 PHP 不干了,直接报错,再次跪了,丫说与 Apache event MPM 下线程不安全(错误丢了改天我努力复现!),所以 PHP 也得换成 PHP-FPM。

apachectl stop
apt-get install php7.0-fpm # Install the php-fpm from your PHP repository. This package name depends on the vendor.
a2enmod proxy_fcgi setenvif
a2enconf php7.0-fpm # Again, this depends on your PHP vendor.
a2dismod php7.0 # This disables mod_php.
a2dismod mpm_prefork # This disables the prefork MPM. Only one MPM can run at a time.
a2enmod mpm_event # Enable event MPM. You could also enable mpm_worker.
apachectl start

最后我没精打采的打开 Chrome DevTools > Network > 勾选上 Protocol,终于看到了 h2 标示,但是静态资源这么还是 http/1.1,WTF?倒腾半天,勾选上 disable cache 强刷,一切都在 h2 下了!

最后看 HTTP 超级不爽,还是 301 跳转到到 HTTPS 下吧:

<VirtualHost *:80>
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
</VirtualHost>

结尾

这次折腾共计 1 天 2 夜,最终算是完美结局了。最初目标是使用 HTTP/2 加速博客访问速度,现在里就可以并观察 Network 是不是静态资源加载基本没有了阻塞了呢。我的目标实现了?并没有…因为之前使用 七牛CDN 加速,国内尚能获的不错的访问速度。但是我的博客尚未备案,七牛 CDN HTTPS 加速需要备案,所以也就告别了七牛 CDN。自己服务器远在异国他乡的美利坚,页面漂洋过海才能展现你的面前,所以 HTPP/2 也没法救啊,这算一次失败的优化么?

参考

  1. https://imququ.com
  2. https://blog.lutty.me/linux/2016-05/enable-apache2-http2-in-ubuntu.html
  3. https://cloud.tencent.com/developer/article/1004924
  4. https://www.zcfy.cc/article/http-2-push-vs-http-preload-dexecure
  5. https://www.nihaoshijie.com.cn/index.php/archives/698/
  6. https://imququ.com/post/http2-and-wpo-2.html

2 comments on "LAMP 启用 HTTP/2 一波三折"

发表评论

电子邮件地址不会被公开。 必填项已用*标注