HTTP2 Server Push

1,HTTP2 的新特性。

关于 HTTP2 的新特性,读着可以参看我之前的文章,这里就不在多说了,本篇文章主要讲一下 server push 这个特性。

HTTP,HTTP2.0,SPDY,HTTPS 你应该知道的一些事

2,Server Push 是什么。

简单来讲就是当用户的浏览器和服务器在建立链接后,服务器主动将一些资源推送给浏览器并缓存起来,这样当浏览器接下来请求这些资源时就直接从缓存中读取,不会在从服务器上拉了,提升了速率。举一个例子就是:

假如一个页面有 3 个资源文件 index.html,index.css,index.js, 当浏览器请求 index.html 的时候,服务器不仅返回 index.html 的内容,同时将 index.css 和 index.js 的内容 push 给浏览器,当浏览器下次请求这 2 两个文件时就可以直接从缓存中读取了。

3,Server Push 原理是什么。

要想了解 server push 原理,首先要理解一些概念。我们知道 HTTP2 传输的格式并不像 HTTP1 使用文本来传输,而是启用了二进制帧 (Frames) 格式来传输,和 server push 相关的帧主要分成这几种类型:

  1. HEADERS frame(请求返回头帧): 这种帧主要携带的 http 请求头信息,和 HTTP1 的 header 类似。
  2. DATA frames(数据帧) : 这种帧存放真正的数据 content,用来传输。
  3. PUSH_PROMISE frame(推送帧): 这种帧是由 server 端发送给 client 的帧,用来表示 server push 的帧,这种帧是实现 server push 的主要帧类型。
  4. RST_STREAM(取消推送帧): 这种帧表示请求关闭帧,简单讲就是当 client 不想接受某些资源或者接受 timeout 时会向发送方发送此帧,和 PUSH_PROMISE frame 一起使用时表示拒绝或者关闭 server push。

Note:HTTP2.0 相关的帧其实包括 10 种帧,正是因为底层数据格式的改变,才为 HTTP2.0 带来许多的特性,帧的引入不仅有利于压缩数据,也有利于数据的安全性和可靠传输性。

了解了相关的帧类型,下面就是具体 server push 的实现过程了:

  1. 由多路复用我们可以知道 HTTP2 中对于同一个域名的请求会使用一条 tcp 链接而用不同的 stream ID 来区分各自的请求。
  2. 当 client 使用 stream 1 请求 index.html 时,server 正常处理 index.html 的请求,并可以得知 index.html 页面还将要会请求 index.css 和 index.js。
  3. server 使用 stream 1 发送 PUSH_PROMISE frame 给 client 告诉 client 我这边可以使用 stream 2 来推送 index.js 和 stream 3 来推送 index.css 资源。
  4. server 使用 stream 1 正常的发送 HEADERS frame 和 DATA frames 将 index.html 的内容返回给 client。
  5. client 接收到 PUSH_PROMISE frame 得知 stream 2 和 stream 3 来接收推送资源。
  6. server 拿到 index.css 和 index.js 便会发送 HEADERS frame 和 DATA frames 将资源发送给 client。
  7. client 拿到 push 的资源后会缓存起来当请求这个资源时会从直接从从缓存中读取。

4,Server Push 怎么用。

既然 server push 这么神奇,那么我们如何使用呢?怎么设置服务器 push 哪些文件呢?

首先并不是所有的服务器都支持 server push,nginx 目前还不支持这个特性,可以在 nginx 的官方博客上得到证实 https://www.nginx.com/blog/http2-r7/,但是 Apache 和 nodejs 都已经支持了 server push 这一个特性,需要说明一点的是 server push 这个特性是基于浏览器和服务器的,所以浏览器并没有提供相应的 js api 来让用户直接操作和控制 push 的内容,所以只能是通过 header 信息和 server 的配置来实现具体的 push 内容,本文主要以 nodejs 来说明具体如何使用 server push 这一特性。

准备工作:下载 nodejs http2 支持,本地启动 nodejs 服务。

1. 首先我们使用 nodejs 搭建基本的 server:


var  http2  =  require('http2');

var  url=require('url');

var  fs=require('fs');

var  mine=require('./mine').types;

var  path=require('path');

var  server  =  http2.createServer({

  key:  fs.readFileSync('./zs/localhost.key'),

  cert:  fs.readFileSync('./zs/localhost.crt')

},  function(request,  response)  {

    var  pathname  =  url.parse(request.url).pathname;

    var  realPath  =  path.join("my",  pathname);    //这里设置自己的文件名称;

    var  pushArray  =  [];

    var  ext  =  path.extname(realPath);

    ext  =  ext  ?  ext.slice(1)  :  'unknown';

    var  contentType  =  mine[ext]  ||  "text/plain";

    if  (fs.existsSync(realPath))  {

        response.writeHead(200,  {

            'Content-Type':  contentType

        });

        response.write(fs.readFileSync(realPath,'binary'));

    }  else  {

      response.writeHead(404,  {

          'Content-Type':  'text/plain'

      });

      response.write("This request URL "  +  pathname  +  " was not found on this server.");

      response.end();

    }

});

server.listen(443,  function()  {

  console.log('listen on 443');

});

这几行代码就是简单搭建一个 nodejs http2 服务,打开 chrome,我们可以看到所有请求都走了 http2,同时也可以验证多路复用的特性。

这里需要注意几点:

  1. 创建 http2 的 nodejs 服务必须时基于 https 的,因为现在主流的浏览器都要支持 SSL/TLS 的 http2,证书和私钥可以自己通过 OPENSSL 生成。
  2. node http2 的相关 api 和正常的 node httpserver 相同,可以直接使用。

2. 设置我们的 server push:

var  pushItem  =  response.push('/css/bootstrap.min.css',  {

 request:  {

            accept:  '*/\*'

 },

      response:  {

            'content-type':  'text/css'

 }

});

pushItem.end(fs.readFileSync('/css/bootstrap.min.css','binary'));

我们设置了 bootstrap.min.css 来通过 server push 到我们的浏览器, 我们可以在浏览器中查看:

可以看到,启动 server push 的资源 timelime 非常快,大大加速了 css 的获取时间。

这里需要注意下面几点:

  1. 我们调用 response.push(), 就是相当于 server 发起了 PUSH_PROMISE frame 来告知浏览器 bootstrap.min.css 将会由 server push 来获取。
  2. response.push() 返回的对象时一个正常的 ServerResponse,end(),writeHeader() 等方法都可以正常调用。
  3. 这里一旦针对某个资源调用 response.push() 即发起 PUSH_PROMISE frame 后,要做好容错机制,因为浏览器在下次请求这个资源时会且只会等待这个 server push 回来的资源,这里要做好超时和容错即下面的代码:

    try  {

        pushItem.end(fs.readFileSync('my/css/bootstrap.min.css','binary'));

        }  catch(e)  {

     response.writeHead(404,  {

     'Content-Type':  'text/plain'

     });

     response.end('request error');

    }

    pushItem.stream.on('error',  function(err){

        response.end(err.message);

    });

    pushItem.stream.on('finish',  function(err){

     console.log('finish');

    });

上面的代码你可能会发现许多和正常 nodejs 的 httpserver 不一样的东西,那就是 stream,其实整个 http2 都是以 stream 为单位,这里的 stream 其实可以理解成一个请求,更多的 api 可以参考:[node-http2](https://github.com/molnarg/node-http2/wiki/Public-API)。
  1. 最后给大家推荐一个老外写的专门服务 http2 的 node server 有兴趣的可以尝试一下。https://gitlab.com/sebdeckers/http2server

5,Server Push 相关问题。

  1. 我们知道现在我们 web 的资源一般都是放在 CDN 上的,那么 CDN 的优势和 server push 的优势有何区别呢,到底是哪个比较快呢?这个问题笔者也一直在研究,本文的相关 demo 都只能算做一个演示,具体的线上实践还在进行中。
  2. 由于 HTTP2 的一些新特性例如多路复用,server push 等等都是基于同一个域名的,所以这可能会对我们之前对于 HTTP1 的一些优化措施例如 (资源拆分域名,合并等等) 不一定适用。
  3. server push 不仅可以用作拉取静态资源,我们的 cgi 请求即 ajax 请求同样可以使用 server push 来发送数据。
  4. 最完美的结果是 CDN 域名支持 HTTP2,web server 域名也同时支持 HTTP2。

参考资料:

  1. HTTP2 官方标准:https://tools.ietf.org/html/rfc7540
  2. 维基百科:https://en.wikipedia.org/wiki/HTTP/2_Server_Push
  3. https://www.nihaoshijie.com.cn/index.php/archives/651
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341

推荐阅读更多精彩内容