如何提升Node.js服务性能

这篇文章给大家介绍如何提升Node.js 服务性能,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

我们提供的服务有:成都网站设计、成都网站制作、微信公众号开发、网站优化、网站认证、浉河ssl等。为上千家企事业单位解决了网站和推广的问题。提供周到的售前咨询和贴心的售后服务,是有科学管理、有技术的浉河网站制作公司

author

如何使用find-my-way 通过 on 方法绑定路由,并且提供了 HTTP 所有方法的简写。

const router = require('./index')()  router.on('GET', '/a', (req, res, params) => {   res.end('{"message": "GET /a"}') }) router.get('/a/b', (req, res, params) => {   res.end('{"message": "GET /a/b"}') }))

其实内部就是通过遍历所有的 HTTP 方法名,然后在原型上扩展的。

Router.prototype.on = function on (method, path, opts, handler) {   if (typeof opts === 'function') {     // 如果 opts 为函数,表示此时的 opts 为 handler     handler = opts     opts = {}   }   // ... } for (var i in http.METHODS) {   const m = http.METHODS[i]   const methodName = m.toLowerCase()   // 扩展方法简写   Router.prototype[methodName] = function (path, handler) {     return this.on(m, path, handler)   } }

绑定的路由可以通过 lookup 调用,只要将原生的 req 和 res 传入 lookup 即可。

const http = require('http')  const server = http.createServer((req, res) => {   // 只要将原生的 req 和 res 传入 lookup 即可   router.lookup(req, res) })   server.listen(3000)

find-my-way 会通过 req.method/req.url 找到对应的 handler,然后进行调用。

Router.prototype.lookup = function lookup (req, res) {   var handle = this.find(req.method, sanitizeUrl(req.url))   if (handle === null) {     return this._defaultRoute(req, res, ctx)   }   // 调用 hendler   return handle.handler(req, res, handle.params) }

路由的添加和查找都基于树结构来实现的,下面我们来看看具体的实现。

Radix Tree

find-my-way 采用了名为 Radix Tree(基数树) 的算法,也被称为 Prefix Tree(前缀树)。Go  语言里常用的 web 框架echo和gin都使用了Radix Tree作为路由查找的算法。

  • 在计算机科学中,基数树,或称压缩前缀树,是一种更节省空间的Trie(前缀树)。对于基数树的每个节点,如果该节点是确定的子树的话,就和父节点合并。

如何提升Node.js 服务性能

Radix Tree

在 find-my-way 中每个 HTTP 方法(GET、POST、PUT ...)都会对应一棵前缀树。

// 方法有所简化... function Router (opts) {   opts = opts || {}   this.trees = {}   this.routes = [] }  Router.prototype.on = function on (method, path, opts, handler) {   if (typeof opts === 'function') {     // 如果 opts 为函数,表示此时的 opts 为 handler     handler = opts     opts = {}   }   this._on(method, path, opts, handler) }  Router.prototype._on = function on (method, path, opts, handler) {   this.routes.push({     method, path, opts, handler,   })   // 调用 _insert 方法   this._insert(method, path, handler)  } Router.prototype._insert = function _insert (method, path, handler) {   // 取出方法对应的 tree   var currentNode = this.trees[method]   if (typeof currentNode === 'undefined') {     // 首次插入构造一个新的 Tree     currentNode = new Node({ method })     this.trees[method] = currentNode   }   while(true) {     // 为 currentNode 插入新的节点...   } }

每个方法对应的树在第一次获取不存在的时候,都会先创建一个根节点,根节点使用默认字符(/)。

如何提升Node.js 服务性能

trees

每个节点的数据结构如下:

// 只保留了一些重要参数,其他的暂时忽略 function Node(options) {   options = options || {}   this.prefix = options.prefix || '/' // 去除公共前缀之后的字符,默认为 /   this.label = this.prefix[0]         // 用于存放其第一个字符   this.method = options.method        // 请求的方法   this.handler = options.handler      // 请求的回调   this.children = options.children || {} // 存放后续的子节点 }

当我们插入了几个路由节点后,树结构的具体构造如下:

router.on('GET', '/a', (req, res, params) => {   res.end('{"message":"hello world"}') }) router.on('GET', '/aa', (req, res, params) => {   res.end('{"message":"hello world"}') }) router.on('GET', '/ab', (req, res, params) => {   res.end('{"message":"hello world"}') })

  如何提升Node.js 服务性能

GET Tree

Node {   label: 'a',   prefix: 'a',   method: 'GET',   children: {     a: Node {       label: 'a',       prefix: 'a',       method: 'GET',       children: {},       handler: [Function]     },     b: Node {       label: 'b',       prefix: 'b',       method: 'GET',       children: {},       handler: [Function]     }   },   handler: [Function] }

如果我们绑定一个名为 /axxx 的路由,为了节约内存,不会生成三个 label 为x 的节点,只会生成一个节点,其 label 为 x,prefix 为  xxx。

router.on('GET', '/a', (req, res, params) => {   res.end('{"message":"hello world"}') }) router.on('GET', '/axxx', (req, res, params) => {   res.end('{"message":"hello world"}') })

 如何提升Node.js 服务性能

GET Tree

Node {   label: 'a',   prefix: 'a',   method: 'GET',   children: {     a: Node {       label: 'x',       prefix: 'xxx',       method: 'GET',       children: {},       handler: [Function]     }   },   handler: [Function] }

插入路由节点

通过之前的代码可以看到, on 方法最后会调用内部的 _insert 方法插入新的节点,下面看看其具体的实现方式:

Router.prototype._insert = function _insert (method, path, handler) {   // 取出方法对应的 tree   var currentNode = this.trees[method]   if (typeof currentNode === 'undefined') {     // 首次插入构造一个新的 Tree     currentNode = new Node({ method })     this.trees[method] = currentNode   }    var len = 0   var node = null   var prefix = ''   var prefixLen = 0   while(true) {     prefix = currentNode.prefix     prefixLen = prefix.length     len = prefixLen     path = path.slice(len)     // 查找是否存在公共前缀     node = currentNode.findByLabel(path)     if (node) {       // 公共前缀存在,复用       currentNode = node       continue     }     // 公共前缀不存在,创建一个     node = new Node({ method: method, prefix: path })     currentNode.addChild(node)   } }

插入节点会调用 Node 原型上的 addChild 方法。

Node.prototype.getLabel = function () {   return this.prefix[0] }  Node.prototype.addChild = function (node) {   var label = node.getLabel() // 取出第一个字符做为 label   this.children[label] = node   return this }

本质是遍历路径的每个字符,然后判断当前节点的子节点是否已经存在一个节点,如果存在就继续向下遍历,如果不存在,则新建一个节点,插入到当前节点。

如何提升Node.js 服务性能

tree

查找路由节点

find-my-way 对外提供了 lookup 方法,用于查找路由对应的方法并执行,内部是通过 find 方法查找的。

Router.prototype.find = function find (method, path, version) {   var currentNode = this.trees[method]   if (!currentNode) return null    while (true) {     var pathLen = path.length     var prefix = currentNode.prefix     var prefixLen = prefix.length     var len = prefixLen     var previousPath = path     // 找到了路由     if (pathLen === 0 || path === prefix) {       var handle = currentNode.handler       if (handle !== null && handle !== undefined) {         return {           handler: handle.handler         }       }     }     // 继续向下查找     path = path.slice(len)     currentNode = currentNode.findChild(path)   } }  Node.prototype.findChild = function (path) {   var child = this.children[path[0]]   if (child !== undefined || child.handler !== null)) {     if (path.slice(0, child.prefix.length) === child.prefix) {       return child     }   }    return null }

查找节点也是通过遍历树的方式完成的,找到节点之后还需要放到 handle 是否存在,存在的话需要执行回调。

关于如何提升Node.js 服务性能就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。


名称栏目:如何提升Node.js服务性能
标题网址:http://azwzsj.com/article/peocoo.html