日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Node.js服務(wù)性能翻倍的秘密之一

前言

網(wǎng)站制作、成都做網(wǎng)站服務(wù)團(tuán)隊(duì)是一支充滿著熱情的團(tuán)隊(duì),執(zhí)著、敏銳、追求更好,是創(chuàng)新互聯(lián)的標(biāo)準(zhǔn)與要求,同時(shí)竭誠為客戶提供服務(wù)是我們的理念。創(chuàng)新互聯(lián)建站把每個(gè)網(wǎng)站當(dāng)做一個(gè)產(chǎn)品來開發(fā),精雕細(xì)琢,追求一名工匠心中的細(xì)致,我們更用心!

用過 Node.js 開發(fā)過的同學(xué)肯定都上手過 koa,因?yàn)樗唵蝺?yōu)雅的寫法,再加上豐富的社區(qū)生態(tài),而且現(xiàn)存的許多 Node.js 框架都是基于 koa 進(jìn)行二次封裝的。但是說到性能,就不得不提到一個(gè)老牌框架:fastify ,聽名字就知道它的特性就是快,官方給出的Benchmarks甚至比 Node.js 原生的 http.Server 還要快。

Benchmarks

性能提升的關(guān)鍵

我們先看看 fastify 是如何啟動(dòng)一個(gè)服務(wù)的。

 
 
 
 
  1. # 安裝 fastify
  2. npm i -S fastify@3.9.1
 
 
 
 
  1. // 創(chuàng)建服務(wù)實(shí)例
  2. const fastify = require('fastify')()
  3. app.get('/', {
  4.   schema: {
  5.     response: {
  6.       // key 為響應(yīng)狀態(tài)碼
  7.       '200': {
  8.         type: 'object',
  9.         properties: {
  10.           hello: { type: 'string' }
  11.         }
  12.       }
  13.     }
  14.   }
  15. }, async () => {
  16.   return { hello: 'world' }
  17. })
  18. // 啟動(dòng)服務(wù)
  19. ;(async () => {
  20.   try {
  21.     const port = 3001 // 監(jiān)聽端口
  22.     await app.listen(port)
  23.     console.info(`server listening on ${port}`)
  24.   } catch (err) {
  25.     console.error(err)
  26.     process.exit(1)
  27.   }
  28. })()

從上面代碼可以看出,fastify 對(duì)請(qǐng)求的響應(yīng)體定義了一個(gè) schema,fastify 除了可以定義響應(yīng)體的 schema,還支持對(duì)如下數(shù)據(jù)定義 schema:

  1. body:當(dāng)為 POST 或 PUT 方法時(shí),校驗(yàn)請(qǐng)求主體;
  2. query:校驗(yàn) url 的 查詢參數(shù);
  3. params:校驗(yàn) url 參數(shù);
  4. response:過濾并生成用于響應(yīng)體的 schema。
 
 
 
 
  1. app.post('/user/:id', {
  2.   schema: {
  3.     params: {
  4.       type: 'object',
  5.       properties: {
  6.        id: { type: 'number' }
  7.       }
  8.     },
  9.     response: {
  10.       // 2xx 表示 200~299 的狀態(tài)都適用此 schema
  11.       '2xx': {
  12.         type: 'object',
  13.         properties: {
  14.           id: { type: 'number' },
  15.           name: { type: 'string' }
  16.         }
  17.       }
  18.     }
  19.   }
  20. }, async (req) => {
  21.   const id = req.params.id
  22.   const userInfo = await User.findById(id)
  23.   // Content-Type 默認(rèn)為 application/json
  24.   return userInfo
  25. })

讓 fastify 性能提升的的秘訣在于,其返回 application/json 類型數(shù)據(jù)的時(shí)候,并沒有使用原生的 JSON.stringify,而是自己內(nèi)部重新實(shí)現(xiàn)了一套 JSON 序列化的方法,這個(gè) schema 就是 JSON 序列化性能翻倍的關(guān)鍵。

如何對(duì) JSON 序列化

在探索 fastify 如何對(duì) JSON 數(shù)據(jù)序列化之前,我們先看看 JSON.stringify 需要經(jīng)過多么繁瑣的步驟,這里我們參考 Douglas Crockford (JSON 格式的創(chuàng)建者)開源的 JSON-js 中實(shí)現(xiàn)的 stringify 方法。

“JSON-js:https://github.com/douglascrockford/JSON-js/blob/master/json2.js

 
 
 
 
  1. // 只展示 JSON.stringify 核心代碼,其他代碼有所省略
  2. if (typeof JSON !== "object") {
  3.   JSON = {};
  4. }
  5. JSON.stringify = function (value) {
  6.   return str("", {"": value})
  7. }
  8. function str(key, holder) {
  9.   var value = holder[key];
  10.   switch(typeof value) {
  11.     case "string":
  12.       return quote(value);
  13.     case "number":
  14.       return (isFinite(value)) ? String(value) : "null";
  15.     case "boolean":
  16.     case "null":
  17.       return String(value);
  18.     case "object":
  19.       if (!value) {
  20.         return "null";
  21.       }
  22.       partial = [];
  23.       if (Object.prototype.toString.apply(value) === "[object Array]") {
  24.         // 處理數(shù)組
  25.         length = value.length;
  26.         for (i = 0; i < length; i += 1) {
  27.           // 每個(gè)元素都需要單獨(dú)處理
  28.           partial[i] = str(i, value) || "null";
  29.         }
  30.         // 將 partial 轉(zhuǎn)成 ”[...]“
  31.         v = partial.length === 0
  32.           ? "[]"
  33.           : "[" + partial.join(",") + "]";
  34.         return v;
  35.       } else {
  36.         // 處理對(duì)象
  37.         for (k in value) {
  38.           if (Object.prototype.hasOwnProperty.call(value, k)) {
  39.             v = str(k, value);
  40.             if (v) {
  41.               partial.push(quote(k) + ":" + v);
  42.             }
  43.           }
  44.         }
  45.         // 將 partial 轉(zhuǎn)成 "{...}"
  46.         v = partial.length === 0
  47.           ? "{}"
  48.          : "{" + partial.join(",") + "}";
  49.         return v;
  50.       }
  51.   }
  52. }

從上面的代碼可以看出,進(jìn)行 JSON 對(duì)象序列化時(shí),需要遍歷所有的數(shù)組與對(duì)象,逐一進(jìn)行類型的判斷,并對(duì)所有的 key 加上 "",而且這里還不包括一些特殊字符的 encode 操作。但是,如果有了 schema 之后,這些情況會(huì)變得簡單很多。fastify 官方將 JSON 的序列化單獨(dú)成了一個(gè)倉庫:fast-json-stringify,后期還引入了 ajv 來進(jìn)行校驗(yàn),這里為了更容易看懂代碼,選擇看比較早期的版本:0.1.0,邏輯比較簡單,便于理解。

“fast-json-stringify@0.1.0:https://github.com/fastify/fast-json-stringify/blob/v0.1.0/index.js

 
 
 
 
  1. function $Null (i) {
  2.   return 'null'
  3. }
  4. function $Number (i) {
  5.   var num = Number(i)
  6.   if (isNaN(num)) {
  7.     return 'null'
  8.   } else {
  9.     return String(num)
  10.   }
  11. }
  12. function $String (i) {
  13.   return '"' + i + '"'
  14. }
  15. function buildObject (schema, code, name) {
  16.   // 序列化對(duì)象 ...
  17. }
  18. function buildArray (schema, code, name) {
  19.   // 序列化數(shù)組 ...
  20. }
  21. function build (schema) {
  22.   var code = `
  23.     'use strict'
  24.     ${$String.toString()}
  25.     ${$Number.toString()}
  26.     ${$Null.toString()}
  27.   `
  28.   var main
  29.   code = buildObject(schema, code, '$main')
  30.   code += `
  31.     ;
  32.     return $main
  33.   `
  34.   return (new Function(code))()
  35. }
  36. module.exports = build

fast-json-stringify 對(duì)外暴露一個(gè) build 方法,該方法接受一個(gè) schema,返回一個(gè)函數(shù)($main),用于將 schema 對(duì)應(yīng)的對(duì)象進(jìn)行序列化,具體使用方式如下:

 
 
 
 
  1. const build = require('fast-json-stringify')
  2. const stringify = build({
  3.   type: 'object',
  4.   properties: {
  5.     id: { type: 'number' },
  6.     name: { type: 'string' }
  7.   }
  8. })
  9. console.log(stringify)
  10. const objString = stringify({
  11.   id: 1, name: 'shenfq'
  12. })
  13. console.log(objString) // {"id":1,"name":"shenfq"}

經(jīng)過 build 構(gòu)造后,返回的序列化方法如下:

 
 
 
 
  1. function $String (i) {
  2.   return '"' + i + '"'
  3. }
  4. function $Number (i) {
  5.   var num = Number(i)
  6.   if (isNaN(num)) {
  7.     return 'null'
  8.   } else {
  9.     return String(num)
  10.   }
  11. }
  12. function $Null (i) {
  13.   return 'null'
  14. }
  15. // 序列化方法
  16. function $main (obj) {
  17.   var json = '{'
  18.   json += '"id":'
  19.   json += $Number(obj.id)
  20.   json += ','
  21.   json += '"name":'
  22.   json += $String(obj.name)
  23.   json += '}'
  24.   return json
  25. }

可以看到,有 schema 做支撐,序列化的邏輯瞬間變得無比簡單,最后得到的 JSON 字符串只保留需要的屬性,簡潔高效。我們回過頭再看看 buildObject 是如何生成 $main 內(nèi)的代碼的:

 
 
 
 
  1. function buildObject (schema, code, name) {
  2.   // 構(gòu)造一個(gè)函數(shù)
  3.   code += `
  4.     function ${name} (obj) {
  5.       var json = '{'
  6.   `
  7.   var laterCode = ''
  8.   // 遍歷 schema 的屬性
  9.   const { properties } = schema
  10.   Object.keys(properties).forEach((key, i, a) => {
  11.     // key 需要加上雙引號(hào)
  12.     code += `
  13.       json += '${$String(key)}:'
  14.     `
  15.     // 通過 nested 轉(zhuǎn)化 value
  16.     const value = properties[key]
  17.     const result = nested(laterCode, name, `.${key}`, value)
  18.     code += result.code
  19.     laterCode = result.laterCode
  20.     if (i < a.length - 1) {
  21.       code += 'json += \',\''
  22.     }
  23.   })
  24.   code += `
  25.       json += '}'
  26.       return json
  27.     }
  28.   `
  29.   code += laterCode
  30.   return code
  31. }
  32. function nested (laterCode, name, key, schema) {
  33.   var code = ''
  34.   var funcName
  35.   // 判斷 value 的類型,不同類型進(jìn)行不同的處理
  36.   const type = schema.type
  37.   switch (type) {
  38.     case 'null':
  39.       code += `
  40.       json += $Null()
  41.       `
  42.       break
  43.     case 'string':
  44.       code += `
  45.       json += $String(obj${key})
  46.       `
  47.       break
  48.     case 'number':
  49.     case 'integer':
  50.       code += `
  51.       json += $Number(obj${key})
  52.       `
  53.       break
  54.     case 'object':
  55.       // 如果 value 為一個(gè)對(duì)象,需要一個(gè)新的方法進(jìn)行構(gòu)造
  56.       funcName = (name + key).replace(/[-.\[\]]/g, '')
  57.       laterCode = buildObject(schema, laterCode, funcName)
  58.       code += `
  59.         json += ${funcName}(obj${key})
  60.       `
  61.       break
  62.     case 'array':
  63.       funcName = (name + key).replace(/[-.\[\]]/g, '')
  64.       laterCode = buildArray(schema, laterCode, funcName)
  65.       code += `
  66.         json += ${funcName}(obj${key})
  67.       `
  68.       break
  69.     default:
  70.       throw new Error(`${type} unsupported`)
  71.   }
  72.   return {
  73.     code,
  74.     laterCode
  75.   }
  76. }

其實(shí)就是對(duì) type 為 "object" 的 properties 進(jìn)行一次遍歷,然后針對(duì) value 不同的類型進(jìn)行二次處理,如果碰到新的對(duì)象,會(huì)構(gòu)造一個(gè)新的函數(shù)進(jìn)行處理。

 
 
 
 
  1. // 如果包含子對(duì)象
  2. const stringify = build({
  3.   type: 'object',
  4.   properties: {
  5.     id: { type: 'number' },
  6.     info: {
  7.       type: 'object',
  8.       properties: {
  9.         age: { type: 'number' },
  10.         name: { type: 'string' },
  11.       }
  12.     }
  13.   }
  14. })
  15. console.log(stringify.toString())
 
 
 
 
  1. function $main (obj) {
  2.   var json = '{'
  3.   json += '"id":'
  4.   json += $Number(obj.id)
  5.   json += ','
  6.   json += '"info":'
  7.   json += $maininfo(obj.info)
  8.   json += '}'
  9.   return json
  10. }
  11. // 子對(duì)象會(huì)通過另一個(gè)函數(shù)處理
  12. function $maininfo (obj) {
  13.   var json = '{'
  14.   json += '"age":'
  15.   json += $Number(obj.age)
  16.   json += ','
  17.   json += '"name":'
  18.   json += $String(obj.name)
  19.   json += '}'
  20.   return json
  21. }

總結(jié)

當(dāng)然,fastify 之所以號(hào)稱自己快,內(nèi)部還有一些其他的優(yōu)化方法,例如,在路由庫的實(shí)現(xiàn)上使用了 Radix Tree 、對(duì)上下文對(duì)象可進(jìn)行復(fù)用(使用 middie 庫)。本文只是介紹了其中的一種體現(xiàn)最重要明顯優(yōu)化思路,希望大家閱讀之后能有所收獲。

本文轉(zhuǎn)載自微信公眾號(hào)「 更了不起的前端」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 更了不起的前端公眾號(hào)。


當(dāng)前題目:Node.js服務(wù)性能翻倍的秘密之一
分享網(wǎng)址:http://www.5511xx.com/article/coiccdo.html