Node.js 靜態伺服器新知

圓兒圈圈發表於2018-03-02

用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%.這樣傳輸就快了,效果就是你點選網址後會很快的顯示出來.當然這也會增加伺服器的負載. 一般伺服器中都安裝有這個功能模組的.

Node.js 靜態伺服器新知

Node.js 靜態伺服器新知

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命令了

相關文章