您好,登錄后才能下訂單哦!
本篇內容主要講解“Node.js服務性能翻倍秘密是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Node.js服務性能翻倍秘密是什么”吧!
前言
用過 Node.js 開發過的同學肯定都上手過 koa,因為他簡單優雅的寫法,再加上豐富的社區生態,而且現存的許多 Node.js 框架都是基于 koa 進行二次封裝的。但是說到性能,就不得不提到一個知名框架:fastify ,聽名字就知道它的特性就是快,官方給出的Benchmarks甚至比 Node.js 原生的 http.Server 還要快。
Benchmarks
性能提升的關鍵
我們先看看 fastify 是如何啟動一個服務的。
# 安裝 fastify npm i -S fastify@3.9.1
// 創建服務實例 const fastify = require('fastify')() app.get('/', { schema: { response: { // key 為響應狀態碼 '200': { type: 'object', properties: { hello: { type: 'string' } } } } } }, async () => { return { hello: 'world' } }) // 啟動服務 ;(async () => { try { const port = 3001 // 監聽端口 await app.listen(port) console.info(`server listening on ${port}`) } catch (err) { console.error(err) process.exit(1) } })()
從上面代碼可以看出,fastify 對請求的響應體定義了一個 schema,fastify 除了可以定義響應體的 schema,還支持對如下數據定義 schema:
鴻蒙官方戰略合作共建——HarmonyOS技術社區
body:當為 POST 或 PUT 方法時,校驗請求主體;
query:校驗 url 的 查詢參數;
params:校驗 url 參數;
response:過濾并生成用于響應體的 schema。
app.post('/user/:id', { schema: { params: { type: 'object', properties: { id: { type: 'number' } } }, response: { // 2xx 表示 200~299 的狀態都適用此 schema '2xx': { type: 'object', properties: { id: { type: 'number' }, name: { type: 'string' } } } } } }, async (req) => { const id = req.params.id const userInfo = await User.findById(id) // Content-Type 默認為 application/json return userInfo })
讓 fastify 性能提升的的秘訣在于,其返回 application/json 類型數據的時候,并沒有使用原生的 JSON.stringify,而是自己內部重新實現了一套 JSON 序列化的方法,這個 schema 就是 JSON 序列化性能翻倍的關鍵。
如何對 JSON 序列化
在探索 fastify 如何對 JSON 數據序列化之前,我們先看看 JSON.stringify 需要經過多么繁瑣的步驟,這里我們參考 Douglas Crockford (JSON 格式的創建者)開源的 JSON-js 中實現的 stringify 方法。
“JSON-js:https://github.com/douglascrockford/JSON-js/blob/master/json2.js
// 只展示 JSON.stringify 核心代碼,其他代碼有所省略 if (typeof JSON !== "object") { JSON = {}; } JSON.stringify = function (value) { return str("", {"": value}) } function str(key, holder) { var value = holder[key]; switch(typeof value) { case "string": return quote(value); case "number": return (isFinite(value)) ? String(value) : "null"; case "boolean": case "null": return String(value); case "object": if (!value) { return "null"; } partial = []; if (Object.prototype.toString.apply(value) === "[object Array]") { // 處理數組 length = value.length; for (i = 0; i < length; i += 1) { // 每個元素都需要單獨處理 partial[i] = str(i, value) || "null"; } // 將 partial 轉成 ”[...]“ v = partial.length === 0 ? "[]" : "[" + partial.join(",") + "]"; return v; } else { // 處理對象 for (k in value) { if (Object.prototype.hasOwnProperty.call(value, k)) { v = str(k, value); if (v) { partial.push(quote(k) + ":" + v); } } } // 將 partial 轉成 "{...}" v = partial.length === 0 ? "{}" : "{" + partial.join(",") + "}"; return v; } } }
從上面的代碼可以看出,進行 JSON 對象序列化時,需要遍歷所有的數組與對象,逐一進行類型的判斷,并對所有的 key 加上 "",而且這里還不包括一些特殊字符的 encode 操作。但是,如果有了 schema 之后,這些情況會變得簡單很多。fastify 官方將 JSON 的序列化單獨成了一個倉庫:fast-json-stringify,后期還引入了 ajv 來進行校驗,這里為了更容易看懂代碼,選擇看比較早期的版本:0.1.0,邏輯比較簡單,便于理解。
“fast-json-stringify@0.1.0:https://github.com/fastify/fast-json-stringify/blob/v0.1.0/index.js
function $Null (i) { return 'null' } function $Number (i) { var num = Number(i) if (isNaN(num)) { return 'null' } else { return String(num) } } function $String (i) { return '"' + i + '"' } function buildObject (schema, code, name) { // 序列化對象 ... } function buildArray (schema, code, name) { // 序列化數組 ... } function build (schema) { var code = ` 'use strict' ${$String.toString()} ${$Number.toString()} ${$Null.toString()} ` var main code = buildObject(schema, code, '$main') code += ` ; return $main ` return (new Function(code))() } module.exports = build
fast-json-stringify 對外暴露一個 build 方法,該方法接受一個 schema,返回一個函數($main),用于將 schema 對應的對象進行序列化,具體使用方式如下:
const build = require('fast-json-stringify') const stringify = build({ type: 'object', properties: { id: { type: 'number' }, name: { type: 'string' } } }) console.log(stringify) const objString = stringify({ id: 1, name: 'shenfq' }) console.log(objString) // {"id":1,"name":"shenfq"}
經過 build 構造后,返回的序列化方法如下:
function $String (i) { return '"' + i + '"' } function $Number (i) { var num = Number(i) if (isNaN(num)) { return 'null' } else { return String(num) } } function $Null (i) { return 'null' } // 序列化方法 function $main (obj) { var json = '{' json += '"id":' json += $Number(obj.id) json += ',' json += '"name":' json += $String(obj.name) json += '}' return json }
可以看到,有 schema 做支撐,序列化的邏輯瞬間變得無比簡單,最后得到的 JSON 字符串只保留需要的屬性,簡潔高效。我們回過頭再看看 buildObject 是如何生成 $main 內的代碼的:
function buildObject (schema, code, name) { // 構造一個函數 code += ` function ${name} (obj) { var json = '{' ` var laterCode = '' // 遍歷 schema 的屬性 const { properties } = schema Object.keys(properties).forEach((key, i, a) => { // key 需要加上雙引號 code += ` json += '${$String(key)}:' ` // 通過 nested 轉化 value const value = properties[key] const result = nested(laterCode, name, `.${key}`, value) code += result.code laterCode = result.laterCode if (i < a.length - 1) { code += 'json += \',\'' } }) code += ` json += '}' return json } ` code += laterCode return code } function nested (laterCode, name, key, schema) { var code = '' var funcName // 判斷 value 的類型,不同類型進行不同的處理 const type = schema.type switch (type) { case 'null': code += ` json += $Null() ` break case 'string': code += ` json += $String(obj${key}) ` break case 'number': case 'integer': code += ` json += $Number(obj${key}) ` break case 'object': // 如果 value 為一個對象,需要一個新的方法進行構造 funcName = (name + key).replace(/[-.\[\]]/g, '') laterCode = buildObject(schema, laterCode, funcName) code += ` json += ${funcName}(obj${key}) ` break case 'array': funcName = (name + key).replace(/[-.\[\]]/g, '') laterCode = buildArray(schema, laterCode, funcName) code += ` json += ${funcName}(obj${key}) ` break default: throw new Error(`${type} unsupported`) } return { code, laterCode } }
其實就是對 type 為 "object" 的 properties 進行一次遍歷,然后針對 value 不同的類型進行二次處理,如果碰到新的對象,會構造一個新的函數進行處理。
// 如果包含子對象 const stringify = build({ type: 'object', properties: { id: { type: 'number' }, info: { type: 'object', properties: { age: { type: 'number' }, name: { type: 'string' }, } } } }) console.log(stringify.toString())
function $main (obj) { var json = '{' json += '"id":' json += $Number(obj.id) json += ',' json += '"info":' json += $maininfo(obj.info) json += '}' return json } // 子對象會通過另一個函數處理 function $maininfo (obj) { var json = '{' json += '"age":' json += $Number(obj.age) json += ',' json += '"name":' json += $String(obj.name) json += '}' return json }
到此,相信大家對“Node.js服務性能翻倍秘密是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。