NodeJs 中的 http、https 和 http2

本人博客文章地址:點(diǎn)擊進(jìn)入

一、 HTTP

1. http.server

  • 創(chuàng)建服務(wù)器
import http from 'http';

const server = http.createServer((req, res) => {
    res.end();
}).listen(8780);
  • 設(shè)置超時(shí)時(shí)間(默認(rèn)是120s)
server.setTimeout(60 * 1000, () => {
    console.log('超時(shí)回調(diào)');
});
  • 服務(wù)器錯(cuò)誤處理(實(shí)現(xiàn)端口被占用時(shí)自動(dòng)更換端口)
import http from 'http';

let port = 80;
const createServer = () => {

    const server = http.createServer((req, res) => {
        res.end("hello");
    }).listen(port);

    console.log("Server running on port:" + port);

    server.on('error', (e) => {
        if (e.code === 'EADDRINUSE') {
            console.log(`端口:${e.port} 被占用`);
            port++;
            createServer();
        }
    });
};
createServer();
  • 關(guān)閉服務(wù)器
server.close(); // 服務(wù)器停止接受新的連接
server.on('close', () => {
    // 服務(wù)器最后一個(gè)連接關(guān)閉時(shí)調(diào)用
});

2. http.IncomingMessage

  • 獲取請求頭
import http from 'http';

const server = http.createServer((req, res) => {
    console.log(req.headers);// 返回的是header中屬性組成的對象
    res.end();
}).listen(8080);
  • 獲取請求方法
import http from 'http';

const server = http.createServer((req, res) => {
    if (req.method == 'GET') {}
    if (req.method == 'POST') {}
    res.end();
}).listen(8080);
  • 獲取請求url與GET、POST參數(shù)
import http from 'http';
import URL from 'url';
import querystring from 'querystring';

const server = http.createServer((req, res) => {
    const url = URL.parse(req.url, true);
    console.log(url); // 獲取URL
    if (req.method == "GET") {
        console.log(url.query);// 獲取GET參數(shù)
    }
    if (req.method == "POST") {
        let body = "";
        req.on('data', function (chunk) {
            body += chunk;
        });
        req.on('end', function () {
            const postQuery = querystring.parse(decodeURI(body));// 獲取POST參數(shù)
        });
    }
    res.end();
}).listen(8080);

3. http.ServerResponse

  • 寫入響應(yīng)頭
import http from 'http';

const server = http.createServer((req, res) => {
    res.setHeader('Content-Type', 'text/html');
    res.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);
    res.end();
}).listen(8780);
  • 寫入響應(yīng)體
import http from 'http';

const server = http.createServer((req, res) => {
    res.write('{name:"liuhuihao"}');
    res.end();
}).listen(8780);

二、 HTTPS

image.png

1. 創(chuàng)建公鑰、私鑰及證書

  • 創(chuàng)建私鑰
openssl genrsa -out privatekey.pem 1024
  • 創(chuàng)建證書簽名請求
openssl req -new -key privatekey.pem -out certrequest.csr
  • 獲取證書,線上證書需要經(jīng)過證書授證中心簽名的文件;下面只創(chuàng)建一個(gè)學(xué)習(xí)使用證書
openssl x509 -req -in certrequest.csr -signkey privatekey.pem -out certificate.pem
  • 創(chuàng)建pfx文件
openssl pkcs12 -export -in certificate.pem -inkey privatekey.pem -out certificate.pfx

2. http.server

  • 創(chuàng)建服務(wù)器
import https from 'https';
import fs from 'fs';

const options = {
    key: fs.readFileSync('dist/privatekey.pem'), // 私鑰
    cert: fs.readFileSync('dist/certificate.pem') // 公鑰
};

https.createServer(options, (req, res) => {
    res.end();
}).listen(8000);

三、 HTTP2

1. 優(yōu)勢

(1)多路復(fù)用 - 雪碧圖、多域名CDN、接口合并

這是一個(gè)HTTP2的演示地址,分別用HTTP/1.1和HTTP/2請求379張圖片,對比出HTTP/2在速度上的優(yōu)勢

image.png

打開控制臺查看網(wǎng)絡(luò)請求,我們可以發(fā)現(xiàn)HTTP/2和HTTP/1.1的明顯區(qū)別

HTTP/1.1:


image.png

HTTP/2:


image.png

由上圖可以看出,多路復(fù)用允許同時(shí)通過單一的 HTTP/2 連接發(fā)起多重的請求-響應(yīng)消息;而HTTP/1.1協(xié)議中,瀏覽器客戶端在同一時(shí)間,針對同一域名下的請求有一定數(shù)量限制。超過限制數(shù)目的請求會(huì)被阻塞

image.png

首部壓縮

  • http/1.x 的 header 由于 cookie 和 user agent很容易膨脹,而且每次都要重復(fù)發(fā)送。http/2使用 encoder 來減少需要傳輸?shù)?header 大小,通訊雙方各自 cache一份 header fields 表,既避免了重復(fù) header 的傳輸,又減小了需要傳輸?shù)拇笮 8咝У膲嚎s算法可以很大的壓縮 header,減少發(fā)送包的數(shù)量從而降低延遲

服務(wù)端推送

  • 服務(wù)端推送是一種在客戶端請求之前發(fā)送數(shù)據(jù)的機(jī)制。在 HTTP/2 中,服務(wù)器可以對客戶端的一個(gè)請求發(fā)送多個(gè)響應(yīng)。舉個(gè)例子,如果一個(gè)請求請求的是index.html,服務(wù)器很可能會(huì)同時(shí)響應(yīng)index.html、logo.jpg 以及 css 和 js 文件,因?yàn)樗揽蛻舳藭?huì)用到這些東西。這相當(dāng)于在一個(gè) HTML 文檔內(nèi)集合了所有的資源。
image.png

2. nodeJS http/2 實(shí)踐

  • 搭建http/2 服務(wù)器
import http2 from 'http2';
import fs from 'fs';

const options = {
    key: fs.readFileSync('dist/privatekey.pem'), // 私鑰
    cert: fs.readFileSync('dist/certificate.pem') // 公鑰
};

http2.createSecureServer(options, (req, res) => {
    res.end();
}).listen(8078);

  • 驗(yàn)證 http/2 多路復(fù)用
import http2 from 'http2';
import fs from 'fs';
import URL from 'url';
import mime from 'mime';

const {HTTP2_HEADER_PATH} = http2.constants;// :path

const options = {
    key: fs.readFileSync('dist/privatekey.pem'), // 私鑰
    cert: fs.readFileSync('dist/certificate.pem') // 公鑰
};

http2.createSecureServer(options, (req, res) => {
    const url = URL.parse(req.url, true);
    if (url.pathname != "/" && url.pathname != "/favicon.ico") {
        const indexObj = getFdAndHeader(url.pathname);
        res.stream.respondWithFD(indexObj.fd, indexObj.headers);
    } else {
        const indexObj = getFdAndHeader('more.html');
        res.stream.respondWithFD(indexObj.fd, indexObj.headers);
    }
}).listen(8078);

const getFdAndHeader = (fileName) => {
    const filePath = "public/" + fileName;
    const fd = fs.openSync(filePath, 'r');
    const stat = fs.fstatSync(fd);
    const headers = {
        'content-length': stat.size,
        'last-modified': stat.mtime.toUTCString(),
        'content-type': mime.lookup(filePath)
    };
    return {fd, headers};
};

效果:


image.png
  • 實(shí)現(xiàn) http/2 服務(wù)端推送
import http2 from 'http2';
import fs from 'fs';
import mime from 'mime';

const {HTTP2_HEADER_PATH} = http2.constants;// :path

const options = {
    key: fs.readFileSync('dist/privatekey.pem'), // 私鑰
    cert: fs.readFileSync('dist/certificate.pem') // 公鑰
};

http2.createSecureServer(options, (req, res) => {
    const indexObj = getFdAndHeader('index.html');
    const jsObj = getFdAndHeader('someJs.js');
    const cssObj = getFdAndHeader('someCss.css');

    push(res.stream, jsObj);
    push(res.stream, cssObj);

    res.stream.respondWithFD(indexObj.fd, indexObj.headers);

}).listen(8078);

function push(stream, obj) {
    stream.pushStream({[HTTP2_HEADER_PATH]: obj.urlPath}, (pushStream) => {
        pushStream.respondWithFD(obj.fd, obj.headers)
    })
}

const getFdAndHeader = (fileName) => {
    const filePath = "public/" + fileName;
    const fd = fs.openSync(filePath, 'r');
    const stat = fs.fstatSync(fd);
    const urlPath = "/" + fileName;
    const headers = {
        'content-length': stat.size,
        'last-modified': stat.mtime.toUTCString(),
        'content-type': mime.lookup(filePath)
    };
    return {fd, headers, urlPath};
};

推送效果:


image.png

無推送效果:


image.png

作者博客地址:https://liuhuihao.com
作者gitHub:https://github.com/geminate

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 最近熱播的電視劇《人民的名義》一直傲居熱搜榜首位,剛開始并沒有多想看的欲望,這名字似乎勾不起我半點(diǎn)興趣。但當(dāng)我看完...
    半象花閱讀 497評論 20 0
  • 中產(chǎn)階級是世界范圍內(nèi)都在廣泛討論的群體,在中國,根據(jù)經(jīng)濟(jì)收入,大約1億人口可以被歸入中產(chǎn)階級的行列,比例固然很低,...
    塵世知行者閱讀 207評論 0 0
  • 我們就那樣靜靜地走著,突然,感覺胳膊上有點(diǎn)濕濕涼涼的東西。我仔細(xì)一看,是雨滴! “啊,下雨啦!”我大叫起來。 李劍...
    小喬xiaoqiao閱讀 450評論 3 3