用 node http 模块搭建服务器一直被用作项目实践及开发, 深入学习后, 对 node 搭建 http 服务器有了新的了解和认识, 写此文总结罗列一下
支持 gzip 压缩
缓存支持 / 控制
yargs 与自动化
Gzip
GZIP 最早由 Jean-loup Gailly 和 Mark Adler 创建, 用于 UNIX 系统的文件压缩我们在 Linux 中经常会用到后缀为. gz 的文件, 它们就是 GZIP 格式的现今已经成为 Internet 上使用非常普遍的一种数据压缩格式, 或者说一种文件格式 HTTP 协议上的 GZIP 编码是一种用来改进 web 应用程序性能的技术大流量的 WEB 站点常常使用 GZIP 压缩技术来让用户感受更快的速度这一般是指 WWW 服务器中安装的一个功能, 当有人来访问这个服务器中的网站时, 服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来. 一般对纯文本内容可压缩到原大小的 40%. 这样传输就快了, 效果就是你点击网址后会很快的显示出来. 当然这也会增加服务器的负载. 一般服务器中都安装有这个功能模块的.
HTTP 请求头 Accept-Encoding 会将客户端能够理解的内容编码方式通常是某种压缩算法进行通知通过内容协商的方式, 服务端会选择一个客户端提议的方式, 使用并在响应报文首部 Content-Encoding 中通知客户端该选择
- Accept-Encoding:gzip, deflate, br// 当前请求支持格式为 gzip,defalte,br
- Content-Encoding:gzip // 当前内容压缩格式为 gzip
可以用命令行设置请求 Accept-Encoding 头
curl -v -H "Accept-Encoding:deflate" http://localhost:8080/msg.txt
http 与 Gzip
- function request(req, res) {
- let {
- pathname
- } = url.parse(req.url); // /msg.txt
- //D:\vipcode\201801\16.http\msg.txt
- let filepath = path.join(__dirname, pathname);
- try {
- // await is only valid in async function
- let statObj = await stat(filepath);
- // 可以根据不同的文件内容类型返回不同的 Content-Type
- res.setHeader('Content-Type', mime.getType(pathname));
- // 为了兼容不同的浏览器, node 把所有的请求头全转成了小写
- let acceptEncoding = req.headers['accept-encoding'];
- // 内容协商
- if (acceptEncoding) {
- if (acceptEncoding.match(/\bgzip\b/)) {
- // 服务器告诉 客户端我用什么压缩方法压缩了
- res.setHeader('Content-Encoding', 'gzip');
- fs.createReadStream(filepath).pipe(zlib.createGzip()).pipe(res);
- } else if (acceptEncoding.match(/\bdeflate\b/)) {
- res.setHeader('Content-Encoding', 'deflate');
- fs.createReadStream(filepath).pipe(zlib.createDeflate()).pipe(res);
- } else {
- fs.createReadStream(filepath).pipe(res);
- }
- } else {
- fs.createReadStream(filepath).pipe(res);
- }
- } catch(e) {
- res.statusCode = 404;
- res.end();
- }
- }
根据拿到的 req.headers['accept-encoding']; 类型对请求的静态资源进行相同类型的压缩
fs.createReadStream(filepath).pipe(zlib.xxx()).pipe(res);
tip: 可以用 util 模块中的 promisify 将一个异步方法转出一个返回 promise 的方法 (node v8 以上)
- let { promisify } = require('util');
- let stat = promisify(fs.stat);
- cache
缓存可以减少对服务器的请求, 加快请求效率
- lastModify
- etag
- expires
lastModify 通过最后修改时间来判断缓存是否可用
- fs.stat(filepath, (err, stat) => {
- if (err) {
- return sendError(req, res);
- } else {
- let ifModifiedSince = req.headers['if-modified-since'];// 请求头资源上次修改时间
- let LastModified = stat.ctime.toGMTString();// 文件上次修改时间
- if (ifModifiedSince == LastModified) {// 如果相等则返回 304
- res.writeHead(304);
- res.end('');
- } else {
- return send(req, res, filepath, stat);
- }
- }
- });
ETag 是实体标签的缩写, 根据实体内容生成的一段 hash 字符串, 可以标识资源的状态当资源发生改变时, ETag 也随之发生变化 ETag 是 Web 服务端产生的, 然后发给浏览器客户端
- fs.stat(file, (err, stat) => {
- if (err) {
- sendError(err, req, res, file, stat);
- } else {
- let ifNoneMatch = req.headers['if-none-match'];
- let etag = crypto.createHash('sha1').update(stat.ctime.toGMTString() + stat.size).digest('hex');
- if (ifNoneMatch) {
- if (ifNoneMatch == etag) {
- res.writeHead(304);
- res.end();
- } else {
- send(req, res, file, etag);
- }
- } else {
- send(req, res, file, etag);
- }
- }
- });
设置服务器响应消息头字段 Expires
- res.setHeader('Expires', expires.toUTCString());
- res.setHeader('Cache-Control', 'max-age=60');
自动化
mac 新建自动化 shell 脚本区别与 windows
- $ touch hello.sh
- $ chmod +x hello.sh
- $ ./hello.sh
安装 yargs
yargs 模块能够解决如何处理命令行参数它也需要安装
npm i yargs --save
编辑 hello.sh
- let yargs = require('yargs');
- // 它可以帮我们解析命令行参数, 把参数数组变成对象的形式
- // -n --name
- let argv = yargs.options('n', {
- alias: 'name',// 别名
- demand: true,// 必填
- default: 'yuanyuan',
- description: '请输入你的姓名'
- })
- .usage('hellp [opitons]')
- .help()// 指定帮助信息
- .example('hello -name yuanyuan', '执行 hello 命令, 然后传入 name 参数为 yuanyuan')
- .alias('h', 'help')
- .argv;
- console.log(argv);
- console.log('hello' + argv.name);
添加 package.json, 并添加以下内容
- {
- "name": "hello",
- "bin": {
- "hello": "hello"
- }
- }
执行 npm link 命令
$ npm link
就可以直接执行 hello 命令了
来源: https://juejin.im/post/5a9913e36fb9a028e52d48fa