Node.js框架fastify為什麼這麼快

fastify是目前Node.js web框架中最快的,為什麼會這麼快?今天就帶大家看看fastify中優化性能的技術點.


路由

fastify中路由底層是 find-my-way 庫, find-my-way是基於Radix Tree(Prefix Tree)數據結構,RadixTree在儲存具有大多相同前綴的字符串查找效率很高.而大多web框架都是使用字典(map)的數據結構.在路由不是很多情況下兩者的效率相差不大,但是隨著路由的增多,特別是相同前綴的路由增多,效率就相差起來了.

<code> └── /
   ├── test (GET)
   │   └── /hello (GET)
   └── hello/world (GET)/<code>

Radix Tree

<code>└── /
├── test (GET)
\t ├── test/hello (GET)
└── hello/world (GET)/<code>

Map

fast-json-stirngify

fastify在json序列化操作使用了fast-json-stringify庫,fast-json-stringify使用JSONSchema描述了序列化的對象的數據類型,遍歷對象使用字符串拼接來構建json數據,後使用flatstr這個hack操作使v8底層的樹形字符串扁平化在轉化為Buffer中提高效率.在實際測試中,在數據量很小的情況下可以比JSON.stringify提高2倍性能,極端的時候能提高10倍.這種性能優勢會隨著數據量增大而縮小.

官方測試結果:

<code>FJS creation x 8,951 ops/sec ±0.51% (92 runs sampled)

JSON.stringify array x 5,146 ops/sec ±0.32% (97 runs sampled)
fast-json-stringify array x 8,402 ops/sec ±0.62% (95 runs sampled)
fast-json-stringify-uglified array x 8,474 ops/sec ±0.49% (93 runs sampled)

JSON.stringify long string x 13,061 ops/sec ±0.25% (98 runs sampled)
fast-json-stringify long string x 13,059 ops/sec ±0.21% (98 runs sampled)
fast-json-stringify-uglified long string x 13,099 ops/sec ±0.14% (98 runs sampled)

JSON.stringify short string x 6,295,988 ops/sec ±0.28% (98 runs sampled)
fast-json-stringify short string x 43,335,575 ops/sec ±1.24% (86 runs sampled)
fast-json-stringify-uglified short string x 40,042,871 ops/sec ±1.38% (93 runs sampled)

JSON.stringify obj x 2,557,026 ops/sec ±0.20% (97 runs sampled)
fast-json-stringify obj x 9,001,890 ops/sec ±0.48% (90 runs sampled)
fast-json-stringify-uglified obj x 9,073,607 ops/sec ±0.41% (94 runs sampled) /<code>

裝飾器

我們在express中,在擴展框架功能是,都是通過中間件給請求上下文(req, res)添加一個屬性/方法,這樣每次請求都會改變res,res的結構,v8底層不能優化為hidden class.而fastify中使用了decorate* api擴展請求上下文時直接把擴展添加到請求上下文的構造函數的prototype中.從而使v8更好優化.

源碼:

<code>function decorateReply (name, fn, dependencies) {
decorate(this[kReply].prototype, name, fn, dependencies)
return this
}

function decorateRequest (name, fn, dependencies) {
assertNotStarted(this, name)
decorate(this[kRequest].prototype, name, fn, dependencies)
return this
}
function decorate (instance, name, fn, dependencies) {
if (instance.hasOwnProperty(name)) {
throw new FST_ERR_DEC_ALREADY_PRESENT(name)
}

if (dependencies) {

checkDependencies(instance, dependencies)
}

if (fn && (typeof fn.getter === 'function' || typeof fn.setter === 'function')) {
Object.defineProperty(instance, name, {
get: fn.getter,
set: fn.setter
})
} else {
instance[name] = fn
}
}
/<code>

對象池化(複用)

在fastify中間件處理上,使用middie庫,其使用了reusify庫做對象複用.在每次新請求進來,會創建請求上下文對象(req,res),循環調用每個中間件,執行中間件時會包裝一個包含req,res,url,context的對象,在請求量很大的時候,就要頻繁的創建回收.使用了對象池化可以把這個對象使用以後進行屬性清空,丟回池中.下次請求可以複用.

緩存

在fastify請求主體解析上(ContentTypeParser),使用了tiny-lru庫,fastify內置了application/json和text/plain解析方法,但是在判斷Content-Type消息頭上不能是等於(===),比如Content-type:application/json;charset=utf8頭,需要正確解析需要使用是否包含(indexOf),但是每次判斷都有消耗,fastify使用lru算法把相同的content-type頭都緩存起來,下次請求直接定位ContentTypeParser處理器.典型的空間換時間.

總結

fastify能做到Node.js web框架最快,就是作者這種錙珠必較.很多性能優化的技巧也值得我們學習.


分享到:


相關文章: