這篇文章給大家介紹如何提升Node.js 服務(wù)性能,內(nèi)容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
我們提供的服務(wù)有:成都網(wǎng)站設(shè)計、成都網(wǎng)站制作、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認證、浉河ssl等。為上千家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的浉河網(wǎng)站制作公司
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 找到對應(yīng)的 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) }
路由的添加和查找都基于樹結(jié)構(gòu)來實現(xiàn)的,下面我們來看看具體的實現(xiàn)。
Radix Tree
find-my-way 采用了名為 Radix Tree(基數(shù)樹) 的算法,也被稱為 Prefix Tree(前綴樹)。Go 語言里常用的 web 框架echo和gin都使用了Radix Tree作為路由查找的算法。
在計算機科學(xué)中,基數(shù)樹,或稱壓縮前綴樹,是一種更節(jié)省空間的Trie(前綴樹)。對于基數(shù)樹的每個節(jié)點,如果該節(jié)點是確定的子樹的話,就和父節(jié)點合并。
Radix Tree
在 find-my-way 中每個 HTTP 方法(GET、POST、PUT ...)都會對應(yīng)一棵前綴樹。
// 方法有所簡化... 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) { // 取出方法對應(yīng)的 tree var currentNode = this.trees[method] if (typeof currentNode === 'undefined') { // 首次插入構(gòu)造一個新的 Tree currentNode = new Node({ method }) this.trees[method] = currentNode } while(true) { // 為 currentNode 插入新的節(jié)點... } }
每個方法對應(yīng)的樹在第一次獲取不存在的時候,都會先創(chuàng)建一個根節(jié)點,根節(jié)點使用默認字符(/)。
trees
每個節(jié)點的數(shù)據(jù)結(jié)構(gòu)如下:
// 只保留了一些重要參數(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é)點后,樹結(jié)構(gòu)的具體構(gòu)造如下:
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"}') })
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"}') })
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) { // 取出方法對應(yīng)的 tree var currentNode = this.trees[method] if (typeof currentNode === 'undefined') { // 首次插入構(gòu)造一個新的 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) { // 公共前綴存在,復(fù)用 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é)點。
tree
查找路由節(jié)點
find-my-way 對外提供了 lookup 方法,用于查找路由對應(yīng)的方法并執(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)。
關(guān)于如何提升Node.js 服務(wù)性能就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
網(wǎng)站標題:如何提升Node.js服務(wù)性能
網(wǎng)站URL:http://jinyejixie.com/article0/peocoo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)頁設(shè)計公司、App設(shè)計、靜態(tài)網(wǎng)站、網(wǎng)站建設(shè)、網(wǎng)站設(shè)計公司、網(wǎng)站收錄
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)