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框架最快,就是作者这种锱珠必较.很多性能优化的技巧也值得我们学习.


分享到:


相關文章: