溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

nodejs如何實現(xiàn)http2推送信息

發(fā)布時間:2021-06-26 10:48:31 來源:億速云 閱讀:998 作者:小新 欄目:大數(shù)據(jù)

這篇文章給大家分享的是有關(guān)nodejs如何實現(xiàn)http2推送信息的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

在http1.x時代,服務(wù)器是不能向客戶端推送消息的,而在http2里面這成為了一個標準。

HTTP/2被設(shè)計為解決HTTP/1.x的許多缺陷。服務(wù)器不能向客戶端推送消息就是一個缺陷,我們用一個案例來解釋一下。

如果一張網(wǎng)頁中依賴了很多資源,如js、css、圖片等。在HTTP/1.x中瀏覽器獲取HTML后,開始快速掃描整張網(wǎng)頁,然后去下載js和css等一些關(guān)鍵資源。而這個過程中有一個瓶頸,那就是瀏覽器如果要掃描html的話必須先加載html,只有加載完html才能掃描出關(guān)鍵資源,而在加載html的過程中,css和js的加載只有等到html加載完成后才能下載,這里產(chǎn)生一個空閑時刻。HTTP1.x的請求流程如圖:

nodejs如何實現(xiàn)http2推送信息

觀察圖片發(fā)現(xiàn),css和js必須等到html加載完成后,瀏覽器才能去請求css和js資源。

為了改善延遲,HTTP/2引入了server push,它允許服務(wù)端推送資源給瀏覽器,在瀏覽器明確地請求之前。一個服務(wù)器是清楚的知道一個頁面需要哪些附加資源的(當(dāng)然這些需要開發(fā)者的配置),在它響應(yīng)瀏覽器第一個請求的時候,可以同步開始推送這些資源。

HTTP2請求流程如圖:

nodejs如何實現(xiàn)http2推送信息

對比兩張圖我們發(fā)現(xiàn),在http2協(xié)議下,如果瀏覽器請求一張網(wǎng)頁,服務(wù)器在返回html資源的時候,還會將css和js資源一同返回。

這就是http2的推送過程,這里是如何實現(xiàn)的呢?這里需要注意,在上篇文章中,我們了解到http2是全雙工通信,并且是基于stream的方式傳輸信息的,當(dāng)瀏覽器請求某個網(wǎng)頁時,在建立了tcp鏈接通道后,這個通道是全雙工通信的實現(xiàn)(全雙工的意思就是通道可以同時處理客戶端的請求和服務(wù)端的響應(yīng)),服務(wù)端在響應(yīng)html內(nèi)容時,同時將css和js以stream的形式push到客戶端。

具體實現(xiàn)流程圖如下:

nodejs如何實現(xiàn)http2推送信息

從上文中,我們了解到雖然html信息和css,js一起返回給客戶端,但是這里需要注意,雖然是一起返回,但是用的是不同的stream,返回html部分有專門的的stream,返回css和js也有相應(yīng)的stream,可以理解為從單位到家只有一條路,但是可以乘坐不同的公交車。

從上圖中我們可以看到一點有意思的規(guī)定,推送數(shù)據(jù)的流的id是偶數(shù),而請求流并針對請求流響應(yīng)的流的id是奇數(shù),我猜測這可能是為了方便區(qū)分推送流和非推送流的區(qū)別。

接著我們利用nodejs的http2模塊來實現(xiàn)一下http2的推送功能,代碼如下:

const http2 = require('http2');const fs = require('fs');const PORT = 8443;//證書與私鑰const  key =fs.readFileSync('./server.key');const  cert= fs.readFileSync('./server.crt');//1、創(chuàng)建服務(wù)器const server = http2.createSecureServer({    key,    cert},onRequest);//2、啟動服務(wù)器server.listen(PORT, (err) => {    if (err) {        console.error(err)        return    }    console.log(`Server listening on ${PORT}`)})//3、設(shè)置request事件函數(shù)function onRequest(req,res){    const reqPath = req.url === '/' ? '/index.html' : req.url    //打印請求流的id和響應(yīng)流的id    console.log("req.stream.id:",req.stream.id);    console.log("res.stream.id:",res.stream.id);    //判斷是否是首頁    if (reqPath === '/index.html') {        //推送1.js        res.stream.pushStream({ ':path': '/1.js' }, (err, pushStream, headers) => {            if (err) throw err;            pushStream.respond({ ':status': 200 });            console.log("pushStream:",pushStream.id)            pushStream.end("console.log(1)");        });        //推送2.js        res.stream.pushStream({ ':path': '/2.js' }, (err, pushStream, headers) => {            if (err) throw err;            console.log("pushStream:",pushStream.id)            pushStream.respond({ ':status': 200 });            pushStream.end("console.log(2)");        });        const fd = fs.openSync('./index.html', 'r');        const stat = fs.fstatSync(fd);        const headers = {            'content-length': stat.size,            'last-modified': stat.mtime.toUTCString(),            'content-type': 'text/html'        };        res.stream.respondWithFD(fd, headers);    }else{        res.end("404")    }}

然后我們再來看一下index.html的代碼:

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body><h2>hello world</h2><script src="/1.js"></script><script src="/2.js"></script></body></html>

index.html的代碼:很簡單,一張網(wǎng)頁引入了1.js和2.js。

服務(wù)端代碼:使用http2模塊創(chuàng)建服務(wù)器和https、http模塊差不多,只不過瀏覽器在支持http2的時候要求必須設(shè)置證書,所以我們需要配置證書和秘鑰。

在request事件函數(shù)中,我們判斷請求的url是否是首頁,如果是首頁的話,我們通過res.stream.pushStream這個方法配置推送信息,本質(zhì)上是配置一個tream,這個stream是Http2Stream 類的實例,res.stream.pushStream需要兩個參數(shù),第一個參數(shù)是一個對象,在這個對象中配置這個流的path,客戶端可以通過這個path使用這個流。

第二個參數(shù)為一個回調(diào)函數(shù),回調(diào)函數(shù)第一個參數(shù)是err,第二個參數(shù)是一個stream,這個stream也是Http2Stream 類的實例。

這個stream是如何設(shè)置的呢?我們通過stream對象上的兩個方法來設(shè)置,respond方法設(shè)置stream的頭部信息,end方法設(shè)置stream的body信息,他們分別對應(yīng)流中的header幀和body幀。

設(shè)置完這兩個需要被推送的流之后,再來設(shè)置主流html的響應(yīng)流,這里我們使用strem實例的另外一個方法respondWidthFD來設(shè)置響應(yīng)流。這個方法需要設(shè)置兩個參數(shù),第一個參數(shù)為文件描述符,第二個參數(shù)為header信息。文件描述符中存儲著流的主體信息,header中保存了流的響應(yīng)頭信息。

從stream兩組設(shè)置方法,我們可以看出流至少包含兩部分信息,header幀和body幀。

代碼中我們分別打印了流的id值,我們運行代碼看一下打印結(jié)果,結(jié)果如圖:

nodejs如何實現(xiàn)http2推送信息

我們可以看到這和我們前面說的一樣,推送流的id是偶數(shù)設(shè)置,非推送流的id為奇數(shù)。

然后我們看一下瀏覽器中network的截圖:

nodejs如何實現(xiàn)http2推送信息

我們看到所有資源都是用http2協(xié)議進行請求響應(yīng)的,而1.js和2.js是服務(wù)器在響應(yīng)html的時候同時push過來的,時間只有1ms。

總結(jié)一下:本篇文章主要講了http2的推送原理,以及如何使用nodejs的http2模塊搭建一臺http2服務(wù)器來實現(xiàn)推送功能。

這里需要注意的幾點如下:

1、http2的推送是基于流和全雙工通信

2、推送流的id是偶數(shù),非推送流的id為奇數(shù)。

3、服務(wù)端推送的內(nèi)容是基于客戶端的需要,這里需要前后端工程師通力合作。

4、在推送數(shù)據(jù)時盡量推送關(guān)鍵性資源,如css、js,關(guān)鍵的背景圖等等,而非關(guān)鍵性資源盡量不要推送。

5、nodejs中stream實例配置的兩種方式,respond和end或者respondWidthFD,不論哪種方式都需要設(shè)置頭信息和body信息。

nodejs如何實現(xiàn)http2推送信息

感謝各位的閱讀!關(guān)于“nodejs如何實現(xiàn)http2推送信息”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI