Node.js 性能調(diào)優(yōu)

服務(wù)性能測(cè)試

調(diào)試 Node 性能首先得找到性能瓶頸所在,包括兩個(gè)方面:

  1. top庸论, 測(cè)試 CPU 和內(nèi)存
  2. iostat, io 設(shè)備的帶寬(硬盤)

Node 性能分析工具

CPU 分析優(yōu)化

工具

profile 探測(cè)器 + ab 壓測(cè)

Node 自帶的 profile

文檔

  1. 啟動(dòng) node 程序,運(yùn)行 node --prof server.js 會(huì)生成 log 文件
  2. 壓測(cè) ab -c100 -t10 http://127.0.0.1:3000/dowload
  3. 執(zhí)行 log 文件:node --prof-process xxx.log > profile.txt

--inspect-brk 參數(shù)

要想在應(yīng)用代碼的第一行斷開铅鲤,可以傳入 --inspect-brk 標(biāo)志

  1. 啟動(dòng) node 程序 node --inspect-brk server.js
  2. 打開 Chrome 的 inspect 切換到 Profiler奥邮,啟動(dòng) CPU 監(jiān)控
  3. 壓測(cè)
  4. 暫停万牺、分析結(jié)果
Heavy 模式
image.png
Chart 模式
image.png

clinic 工具

npm install clinic -g

根據(jù)性能分析報(bào)告優(yōu)化程序

Javascript 代碼性能優(yōu)化

計(jì)算性能優(yōu)化的本質(zhì)

  1. 減少不必要的計(jì)算
  2. 空間換取時(shí)間

HTTP 服務(wù)性能優(yōu)化準(zhǔn)則

  1. 提前計(jì)算(計(jì)算是否可以提前到服務(wù)啟動(dòng)階段


    image.png

內(nèi)存分析優(yōu)化

GC
新生代,容量小洽腺,垃圾回收快
老生代脚粟,容量大,垃圾回收慢
減少內(nèi)存使用已脓,提高服務(wù)性能

不要出現(xiàn)內(nèi)存泄露珊楼。

分析內(nèi)存使用情況

--inspect-brk 調(diào)試

  1. node --inspect-brk-server.js
  2. 打開 chrome 控制面板,切到 Memory
  3. 壓測(cè)
  4. 壓測(cè)過(guò)程中抓取快照

為了便于直觀的觀察內(nèi)存的占用度液,在請(qǐng)求中創(chuàng)建數(shù)組 生成 長(zhǎng)度為 100w 的數(shù)組

壓測(cè):

ab -c100 -t10 http://127.0.0.1:3000/api/1000000

壓測(cè)(請(qǐng)求)過(guò)程中抓取的快照 內(nèi)存占用(318M)


image.png

壓測(cè)(請(qǐng)求)結(jié)束后抓取的快照 內(nèi)存占用(7.7M)


image.png

比較兩次快照


image.png

制造內(nèi)存泄漏的場(chǎng)景

創(chuàng)建不被釋放的數(shù)組厕宗,每次請(qǐng)求往數(shù)組中添加元素

壓測(cè)前后的內(nèi)存使用情況


image.png

都是沒(méi)有釋放的數(shù)組,還指出了變量名堕担,執(zhí)行棧(函數(shù))


image.png

優(yōu)化內(nèi)存

  1. Buffer 分配內(nèi)存

  2. 自己編寫 c++插件

  3. 子進(jìn)程與線程

    • 進(jìn)程
    • 操作系統(tǒng)掛載運(yùn)行程序的單元
    • 擁有獨(dú)立的資源已慢,比如內(nèi)存
    • 線程
    • 進(jìn)行運(yùn)算調(diào)度的單元
    • 進(jìn)程內(nèi)的線程共享進(jìn)程內(nèi)的資源
  4. Node 子進(jìn)程和線程

    • 主線程運(yùn)行 V8 和 js
    • 多個(gè)子線程通過(guò)事件循環(huán)被調(diào)度
    • 使用子進(jìn)程或線程利用更多的 CPU 資源
  5. 集群模式

    • 集群模式可以監(jiān)聽(tīng)相同的端口
    • lib > net.js > listenInCluster 方法

child_process

// worker.js
const http = require('http')
const port = Math.round((Math.random() + 1) * 1000)

http
  .createServer((req, res) => {
    res.end(port + '')
  })
  .listen(port, () => {
    console.log('runing: ', port)
  })

process.on('message', data => {
  console.log('child', port, data)
  process.send('from child --- ' + port)
})
// master.js
const os = require('os')
const { fork } = require('child_process')
const cpus = os.cpus().length

const createWorker = () => {
  const child_process = fork(__dirname + '/worker.js') // ChildProcess 的實(shí)例,其實(shí)還是發(fā)布訂閱模式 基于 Event 事件實(shí)現(xiàn)的

  // 向子進(jìn)程發(fā)送消息
  child_process.send('from master!')

  // 監(jiān)聽(tīng)子進(jìn)程消息
  child_process.on('message', data => {
    console.log('from child, port is --- ', data)
  })

  // 子進(jìn)程退出時(shí)重啟
  child_process.on('exit', () => {
    console.log('child_process is died --- ' + child_process.pid)
    createWorker()
  })

  // 子進(jìn)程無(wú)法被復(fù)制創(chuàng)建霹购、殺死佑惠、發(fā)送消息時(shí)觸發(fā)
  child_process.on('error', () => {
    console.log('')
  })
}

for (let i = 0; i < cpus; i++) {
  createWorker()
}

// 創(chuàng)建子進(jìn)程后主進(jìn)程沒(méi)有退出,如果和子進(jìn)程沒(méi)有事件回調(diào)交互的話需要手動(dòng)退出主進(jìn)程齐疙。所以這就是為什么一般根據(jù)cpu核數(shù)創(chuàng)建子進(jìn)程膜楷,因?yàn)樽舆M(jìn)程開啟后主進(jìn)程退出了。
// setTimeout(() => {
//   process.exit(1)
// }, 2000)

cluster

// app.js
const http = require('http')

http
  .createServer((req, res) => {
    res.writeHead(200)
    res.end('hello world')
  })
  .listen(8000)

process.on('message', msg => {
  if (msg === 'ping') {
    process.send('pang')
  }
})

// 處理沒(méi)有捕獲到的錯(cuò)誤
process.on('uncaughtException', err => {
  console.error(err)
  process.exit(1)
})

// 監(jiān)聽(tīng)內(nèi)存泄漏
setInterval(() => {
  if (process.memoryUsage.memoryUsage().rss > 700 * 1024 * 1024) {
    console.log('memory leak')
    process.exit(1)
  }
}, 5000)
// index.js
const cluster = require('cluster')
const process = require('process')
const cpus = require('os').cpus().length

const createWorker = () => {
  let count = 0
  const worker = cluster.fork()

  // 監(jiān)聽(tīng)子進(jìn)程是否還在工作
  setInterval(() => {
    worker.send('ping')
    count++

    if (count > 3) {
      worker.exit(1)
    }
  }, 3000)

  worker.on('message', msg => {
    if (msg === 'pang') {
      count--
    }
  })
}

if (cluster.isPrimary) {
  console.log(`Primary ${process.pid} is running`)

  // Fork workers
  for (let i = 0; i < cpus / 2; i++) {
    createWorker()
  }

  // 監(jiān)聽(tīng)子進(jìn)程狀態(tài)
  cluster.on('exit', (worker, code, signal) => {
    console.log(`worker ${worker.process.pid} died`)

    setTimeout(() => {
      createWorker()
    }, 5000)
  })
} else {
  require('./app')
}

架構(gòu)優(yōu)化

動(dòng)靜分離

靜態(tài)內(nèi)容

Q:基本不會(huì)變動(dòng),也不會(huì)因?yàn)檎?qǐng)求參數(shù)的不同而變化的
A: 使用 CDN 分發(fā),HTTP 緩存

動(dòng)態(tài)內(nèi)容

Q:各種因?yàn)檎?qǐng)求參數(shù)不同而變動(dòng)注益,且變動(dòng)的數(shù)量幾乎不可枚舉
A:用大量的源站機(jī)器承載,結(jié)合反響代理進(jìn)行負(fù)載均衡

反向代理和緩存服務(wù)

Nginx 反向代理
redis 緩存

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末特愿,一起剝皮案震驚了整個(gè)濱河市仲墨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌揍障,老刑警劉巖目养,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異毒嫡,居然都是意外死亡癌蚁,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門审胚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)匈勋,“玉大人,你說(shuō)我怎么就攤上這事膳叨∏⒔啵” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵菲嘴,是天一觀的道長(zhǎng)饿自。 經(jīng)常有香客問(wèn)我,道長(zhǎng)龄坪,這世上最難降的妖魔是什么昭雌? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮健田,結(jié)果婚禮上烛卧,老公的妹妹穿的比我還像新娘。我一直安慰自己妓局,他們只是感情好总放,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著好爬,像睡著了一般局雄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上存炮,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天炬搭,我揣著相機(jī)與錄音,去河邊找鬼穆桂。 笑死宫盔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的享完。 我是一名探鬼主播飘言,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼驼侠!你這毒婦竟也來(lái)了姿鸿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤倒源,失蹤者是張志新(化名)和其女友劉穎苛预,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體笋熬,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡热某,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了胳螟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昔馋。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖糖耸,靈堂內(nèi)的尸體忽然破棺而出秘遏,到底是詐尸還是另有隱情,我是刑警寧澤嘉竟,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布邦危,位于F島的核電站,受9級(jí)特大地震影響舍扰,放射性物質(zhì)發(fā)生泄漏倦蚪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一边苹、第九天 我趴在偏房一處隱蔽的房頂上張望陵且。 院中可真熱鬧,春花似錦个束、人聲如沸慕购。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)脓钾。三九已至,卻和暖如春桩警,著一層夾襖步出監(jiān)牢的瞬間可训,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工捶枢, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留握截,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓烂叔,卻偏偏與公主長(zhǎng)得像谨胞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蒜鸡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容