溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何提升Node.js 服務性能

發(fā)布時間:2021-07-21 09:35:19 來源:億速云 閱讀:125 作者:Leah 欄目:web開發(fā)

這篇文章給大家介紹如何提升Node.js 服務性能,內(nèi)容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

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"}') }))

其實內(nèi)部就是通過遍歷所有的 HTTP 方法名,然后在原型上擴展的。

Router.prototype.on = function on (method, path, opts, handler) {   if (typeof opts === 'function') {     // 如果 opts 為函數(shù),表示此時的 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 調(diào)用,只要將原生的 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,然后進行調(diào)用。

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)   }   // 調(diào)用 hendler   return handle.handler(req, res, handle.params) }

路由的添加和查找都基于樹結構來實現(xiàn)的,下面我們來看看具體的實現(xiàn)。

Radix Tree

find-my-way 采用了名為 Radix Tree(基數(shù)樹) 的算法,也被稱為 Prefix Tree(前綴樹)。Go  語言里常用的 web 框架echo和gin都使用了Radix Tree作為路由查找的算法。

  • 在計算機科學中,基數(shù)樹,或稱壓縮前綴樹,是一種更節(jié)省空間的Trie(前綴樹)。對于基數(shù)樹的每個節(jié)點,如果該節(jié)點是確定的子樹的話,就和父節(jié)點合并。

如何提升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 為函數(shù),表示此時的 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,   })   // 調(diào)用 _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 插入新的節(jié)點...   } }

每個方法對應的樹在第一次獲取不存在的時候,都會先創(chuàng)建一個根節(jié)點,根節(jié)點使用默認字符(/)。

如何提升Node.js 服務性能

trees

每個節(jié)點的數(shù)據(jù)結構如下:

// 只保留了一些重要參數(shù),其他的暫時忽略 function Node(options) {   options = options || {}   this.prefix = options.prefix || '/' // 去除公共前綴之后的字符,默認為 /   this.label = this.prefix[0]         // 用于存放其第一個字符   this.method = options.method        // 請求的方法   this.handler = options.handler      // 請求的回調(diào)   this.children = options.children || {} // 存放后續(xù)的子節(jié)點 }

當我們插入了幾個路由節(jié)點后,樹結構的具體構造如下:

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 的路由,為了節(jié)約內(nèi)存,不會生成三個 label 為x 的節(jié)點,只會生成一個節(jié)點,其 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] }

插入路由節(jié)點

通過之前的代碼可以看到, on 方法最后會調(diào)用內(nèi)部的 _insert 方法插入新的節(jié)點,下面看看其具體的實現(xiàn)方式:

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     }     // 公共前綴不存在,創(chuàng)建一個     node = new Node({ method: method, prefix: path })     currentNode.addChild(node)   } }

插入節(jié)點會調(diào)用 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 }

本質(zhì)是遍歷路徑的每個字符,然后判斷當前節(jié)點的子節(jié)點是否已經(jīng)存在一個節(jié)點,如果存在就繼續(xù)向下遍歷,如果不存在,則新建一個節(jié)點,插入到當前節(jié)點。

如何提升Node.js 服務性能

tree

查找路由節(jié)點

find-my-way 對外提供了 lookup 方法,用于查找路由對應的方法并執(zhí)行,內(nèi)部是通過 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         }       }     }     // 繼續(xù)向下查找     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 }

查找節(jié)點也是通過遍歷樹的方式完成的,找到節(jié)點之后還需要放到 handle 是否存在,存在的話需要執(zhí)行回調(diào)。

關于如何提升Node.js 服務性能就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI