HTTP/2 服务器推送 - 入门指南

yufei       5 年, 9 月 前       1328

HTTP/2 的主要作用就是提高网页的性能

HTTP 中的头部字段是直接发送文本格式的,但在 HTTP/2 中则会经过压缩后再发送。过去,在相同的 TCP 连接中,只有当上一个响应发送完成后,服务器才会开始发送下一个响应。但在 HTTP/2 中,则可以一起发送多个响应。

使用 HTTP/2 协议时,服务器端推送是唯一要求开发人员自己配置的功能,而对于其它功能,游览器和 Web 服务器都可以自动实现。

本文接下来的章节,将会介绍 HTTP/2 服务器端推送的原理和配置方法

传统的方法

假设有一个非常简单的 HTML 页面 index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
  <link rel="stylesheet" href="forum.css" />
</head>
<body>
  <h1>你好,世界</h1>
  <p>简单教程,简单编程</p>
  <img src="img1.jpg">
</body>
</html>

该页面包含了一个样式表 forum.css 和一张简单的图片 img1.jpg。当我们访问这个页面时,浏览器会发起三个请求。第一个请求就是 index.html

GET /index.html HTTP/1.1

服务器端接收到这个请求后,就会发送 index.html 的源码给浏览器。浏览器接收到响应后,解析然后会判断出该页面包含了样式表和图片,于是,就会继续发起另外两个请求

GET forum.css HTTP/1.1
GET img1.jpg HTTP/1.1

这就是传统的 Web 请求方法。这样的方法有两个问题:

  1. 首先,它至少需要少两轮的 HTTP 通讯。
  2. 其次,在收到样式表之前,页面会短暂的显示 HTTP 标签的默认样式,一旦这种显示超过 2 秒,用户体验将非常不友好

对传统方法的改进

一种解决方案是将外部的资源整合到 Web 文件中以减少 HTTP 请求。例如,将样式表的内容放入<style> 标记中,并将图像更改为 Base64 编码的 DATA URL

另一种解决方案就是预加载资源。也就是使用某些标记,提前告诉浏览器应该立即下载某些资源。例如,上面的代码可以改写为如下形式

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <link rel="preload" href="forum.css" as="style">
    <link rel="preload" href="img1.jpg" as="image">
    <link rel="stylesheet" href="forum.css" />
</head>
<body>
  <h1>你好,世界</h1>
  <p>简单教程,简单编程</p>
  <img src="img1.jpg">
</body>
</html>

对于上面这个范例,其实 preload 命令是不会发生啥作用的。但是,如果前一个网页使用该命令预加载下一个网页所需的资源,那么用户在打开下一个网页时会感觉很快

这两种方法看起来能解决我们的问题,但是它们也有几个缺点:

  1. 虽然第一种方法减少了 HTTP 请求,但它将不同类型的代码合并到一个文件中,这不利于代码优化。
  2. 第二种方法只是提前下载,不会减少 HTTP 请求。

服务器端推送

服务器推送的作用是服务器将各种资源推送到浏览器,尽管浏览器可能尚未收到来自服务器的任何请求。

例如,浏览器可能只是发起了对 index.html 的请求,但服务器可以将 index.htmlforum.cssimg1.jpg 一起发送给浏览器。在这种情况下,只需要进行一轮 HTTP 通信,浏览器将获得所有资源并提高性能

Nginx 实现

1.13.9 版本开始,Nginx 开始支持服务器端推送。

我们假设你已经安装好了高于 1.13.9 的 Nginx ,且配置文件位置为 /usr/local/nginx/conf/nginx.conf。并把 localhost 的域名绑定到了 /www/localhost/html 目录

server {
    listen 80 http2;
    server_name  localhost;

    location / {
      root   /www/localhost/html;
      index  index.html index.htm;
    }
}

首先,移除可能存在的 /www/localhost/html/index.html 文件,然后创建一个新的 /www/locahost/html/index.html 文件然后输入我们刚开始那个范例中的代码,也就是

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="forum.css" />
</head>
<body>
    <h1>你好,世界</h1>
    <p>简单教程,简单编程</p>
    <img src="img1.jpg">
</body>
</html>

其次,在 /www/localhost/html 目录下新创建两个文件 forum.cssimg1.jpg。样式文件 forum.css 的内容如下

h1 {
  color: red;
}

p {
    color:#111;
    line-height:1.5;
}

img {
    height:100px;
}

而对于 img1.jpg ,你可以直接下载 https://www.twle.cn/static/i/img1.jpg

最后,打开 Nginx 配置文件 /usr/local/nginx/conf/nginx.conf 输入以下内容

server {
    listen 80 http2;
    server_name  localhost;

    location / {
      root   /www/localhost/html;
      index  index.html index.htm;
      http2_push /forum.css;
      http2_push /img1.jpg;
    }
}

实际上,它只是在代码末尾添加了两行 http2_push 命令,如果用户请求的是 / ,那么服务器会同时推送 forum.cssimg1.jpg

然后我们使用命名 nginx -s reload 重新加载配置文件,打开浏览器,输入 http://localhost/ 我们就可以看到下面的页面

在该页面上我们并不能直接看到服务器端推送。我们必须打开 「 开发者工具 」 然后切换到 「 network 面板 」下。然后我们就会看到只有发送了一个请求,但 forum.cssimg1.jpg 都被推送了

HTTP/2 Apache 实现

在 Apache Web 服务器中实现 HTTP/2 是类似的。我们可以在配置文件 httpd.conf.htaccess 中添加以下配置来打开 HTTP/2 推送

<FilesMatch "\index.html$">
    Header add Link "</styles.css>; rel=preload; as=style"
    Header add Link "</img1.jpg>; rel=preload; as=image"
</FilesMatch>

HTTP/2 后端实现

我们上面所讲的 HTTP/2 服务器端推送需要写入服务器配置文件中。这显然很不方便,因为每次进行更改时都必须重新启动服务。此外,也不应将应用程序和服务器配置放在一起。

还有另一种方式可以实现服务器端推送。后端应用程序可以生成头部字段 Link 命令。一旦服务器发现了该头部信息,就会启用服务器推送

Link: </styles.css>; rel=preload; as=style

如果我们想要推送多个资源,可以采用下面这种格式

Link: </styles.css>; rel=preload; as=style, </img1.jpg>; rel=preload; as=image

这样,我们的 Nginx 配置文件就可以改成如下格式

server {
    listen 80 ssl http2;

    # ...

    root /var/www/html;

    location = / {
        proxy_pass http://upstream;
        http2_push_preload on;
    }
}

如果服务器或浏览器不支持 HTTP/2,则浏览器会预加载指定的资源文件以处理头部信息。

实际上,头部消息是由预加载标准提出的,其语法和 as 属性值都写在该标准中

缓存问题

但是,服务器端推送存在一个非常令人头疼的问题:如果浏览器已经缓存了需要被推送的资源,那么服务器端推送这个资源就会浪费带宽。即使需要被推送的文件的版本需要更新,浏览器也将会优先使用本地的缓存

一种解决方案是仅为首次使用的用户启用服务器推送。以下是 Nginx 官方给出的示例,它根据 cookie 确定是否是第一次访问

server {
    listen 80 http2 default_server;

    root /www/localhost/html;
    http2_push_preload on;

    location = /demo.html {
        add_header Set-Cookie "session=1";
        add_header Link $resources;
    }
}


map $http_cookie $resources {
    "~*session=1" "";
    default "</style.css>; as=style; rel=preload";
}

性能增强

服务器推送可以提高性能。作为在线评估的结果,启用该功能比未启用时快 8%,比将所有资源都嵌入在网页中的 HTTP/1 快 5%

可以看出,时间并没有减少很多,大约是几百毫秒。此外,不建议一次推送太多资源,这会影响性能,因为浏览器必须处理所有推送的资源。仅推送 CSS 样式表可能是一个不错的选择

目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.