NodeJs 中的 http、https 和 http2

本人博客文章地址:点击进入

一、 HTTP

1. http.server

  • 创建服务器
import http from 'http';

const server = http.createServer((req, res) => {
    res.end();
}).listen(8780);
  • 设置超时时间(默认是120s)
server.setTimeout(60 * 1000, () => {
    console.log('超时回调');
});
  • 服务器错误处理(实现端口被占用时自动更换端口)
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();
  • 关闭服务器
server.close(); // 服务器停止接受新的连接
server.on('close', () => {
    // 服务器最后一个连接关闭时调用
});

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参数
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参数
    }
    if (req.method == "POST") {
        let body = "";
        req.on('data', function (chunk) {
            body += chunk;
        });
        req.on('end', function () {
            const postQuery = querystring.parse(decodeURI(body));// 获取POST参数
        });
    }
    res.end();
}).listen(8080);

3. http.ServerResponse

  • 写入响应头
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);
  • 写入响应体
import http from 'http';

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

二、 HTTPS

image.png

1. 创建公钥、私钥及证书

  • 创建私钥
openssl genrsa -out privatekey.pem 1024
  • 创建证书签名请求
openssl req -new -key privatekey.pem -out certrequest.csr
  • 获取证书,线上证书需要经过证书授证中心签名的文件;下面只创建一个学习使用证书
openssl x509 -req -in certrequest.csr -signkey privatekey.pem -out certificate.pem
  • 创建pfx文件
openssl pkcs12 -export -in certificate.pem -inkey privatekey.pem -out certificate.pfx

2. http.server

  • 创建服务器
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. 优势

(1)多路复用 - 雪碧图、多域名CDN、接口合并

这是一个HTTP2的演示地址,分别用HTTP/1.1和HTTP/2请求379张图片,对比出HTTP/2在速度上的优势

image.png

打开控制台查看网络请求,我们可以发现HTTP/2和HTTP/1.1的明显区别

HTTP/1.1:


image.png

HTTP/2:


image.png

由上图可以看出,多路复用允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息;而HTTP/1.1协议中,浏览器客户端在同一时间,针对同一域名下的请求有一定数量限制。超过限制数目的请求会被阻塞

image.png

首部压缩

  • http/1.x 的 header 由于 cookie 和 user agent很容易膨胀,而且每次都要重复发送。http/2使用 encoder 来减少需要传输的 header 大小,通讯双方各自 cache一份 header fields 表,既避免了重复 header 的传输,又减小了需要传输的大小。高效的压缩算法可以很大的压缩 header,减少发送包的数量从而降低延迟

服务端推送

  • 服务端推送是一种在客户端请求之前发送数据的机制。在 HTTP/2 中,服务器可以对客户端的一个请求发送多个响应。举个例子,如果一个请求请求的是index.html,服务器很可能会同时响应index.html、logo.jpg 以及 css 和 js 文件,因为它知道客户端会用到这些东西。这相当于在一个 HTML 文档内集合了所有的资源。
image.png

2. nodeJS http/2 实践

  • 搭建http/2 服务器
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);

  • 验证 http/2 多路复用
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
  • 实现 http/2 服务端推送
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

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

推荐阅读更多精彩内容

  • 最近热播的电视剧《人民的名义》一直傲居热搜榜首位,刚开始并没有多想看的欲望,这名字似乎勾不起我半点兴趣。但当我看完...
    半象花阅读 463评论 20 0
  • 中产阶级是世界范围内都在广泛讨论的群体,在中国,根据经济收入,大约1亿人口可以被归入中产阶级的行列,比例固然很低,...
    尘世知行者阅读 191评论 0 0
  • 我们就那样静静地走着,突然,感觉胳膊上有点湿湿凉凉的东西。我仔细一看,是雨滴! “啊,下雨啦!”我大叫起来。 李剑...
    小乔xiaoqiao阅读 433评论 3 3