您好,登錄后才能下訂單哦!
這篇文章主要介紹“如何獲取Node性能監控指標”,在日常操作中,相信很多人在如何獲取Node性能監控指標問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何獲取Node性能監控指標”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
服務器的性能瓶頸通常為以下幾個:
CPU 使用率
CPU 負載(load)
內存
磁盤
I/O
吞吐量 (Throughput)
每秒查詢率 QPS(Query Per Second)
日志監控/真實QPS
響應時間
進程監控
CPU 使用率與CPU 負載,這兩個從一定程度上都可以反映一臺機器的繁忙程度。
CPU 使用率是運行的程序占用的 CPU 資源,表示機器在某個時間點的運行程序的情況。使用率越高,說明機器在這個時間上運行了很多程序,反之較少。使用率的高低與 CPU 強弱有直接關系。我們先了解一下相關的API和一些名詞解釋,幫助我們理解獲取CPU使用率的代碼。
os.cpus()
返回包含有關每個邏輯 CPU 內核的信息的對象數組。
model: 一個字符串,指定CPU內核的型號。
speed: 一個數字,指定CPU內核的速度(以MHz為單位)。
times: 包含以下屬性的對象:
user CPU 在用戶模式下花費的毫秒數。
nice CPU 在良好模式下花費的毫秒數。
sys CPU 在系統模式下花費的毫秒數。
idle CPU 在空閑模式下花費的毫秒數。
irq CPU 在中斷請求模式下花費的毫秒數。
注意: 的nice
值僅用于POSIX。在Windows操作系統上,nice
所有處理器的值始終為0。
大家看到user,nice字段,有些同學就優點懵逼了,我也是,所以仔細查詢了一下其意義,請接著。
user 表示 CPU 運行在 用戶態 的時間占比。
應用進程執行分為 用戶態 以及 內核態 : CPU 在用戶態執行應用進程自身的代碼邏輯,通常是一些 邏輯 或 數值計算 ; CPU 在內核態執行進程發起的 系統調用 ,通常是響應進程對資源的請求。
用戶空間程序是任何不屬于內核的進程。 Shell、編譯器、數據庫、Web 服務器以及與桌面相關的程序都是用戶空間進程。 如果處理器沒有空閑,那么大部分 CPU 時間應該花在運行用戶空間進程上是很正常的。
nice 表示 CPU 運行在 低優先級用戶態 的時間占比,低優先級意味著進程 nice 值小于 0 。
user 表示 CPU 運行在 內核態 的時間占比。
一般而言, 內核態 CPU 使用率不應過高,除非應用進程發起大量系統調用。如果太高,表示系統調用時間長,例如是IO操作頻繁。
idle 表示 CPU 在空閑狀態的時間占比,該狀態下 CPU 沒有任何任務可執行。
irq 表示 CPU 處理 硬件中斷 的時間占比。
網卡中斷 是一個典型的例子:網卡接到數據包后,通過硬件中斷通知 CPU 進行處理。 如果系統網絡流量非常大,則可觀察到 irq 使用率明顯升高。
用戶態小于70%,內核態小于35%且整體小于70%,可以算作健康狀態。
以下示例說明了Node.js中os.cpus()方法的使用:
范例1:
// Node.js program to demonstrate the // os.cpus() method // Allocating os module const os = require('os'); // Printing os.cpus() values console.log(os.cpus());
輸出:
[ { model:'Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz', speed:2712, times: { user:900000, nice:0, sys:940265, idle:11928546, irq:147046 } }, { model:'Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz', speed:2712, times: { user:860875, nice:0, sys:507093, idle:12400500, irq:27062 } }, { model:'Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz', speed:2712, times: { user:1273421, nice:0, sys:618765, idle:11876281, irq:13125 } }, { model:'Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz', speed:2712, times: { user:943921, nice:0, sys:460109, idle:12364453, irq:12437 } } ]
下面是如何獲取cpu利用率的代碼
const os = require('os'); const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); class OSUtils { constructor() { this.cpuUsageMSDefault = 1000; // CPU 利用率默認時間段 } /** * 獲取某時間段 CPU 利用率 * @param { Number } Options.ms [時間段,默認是 1000ms,即 1 秒鐘] * @param { Boolean } Options.percentage [true(以百分比結果返回)|false] * @returns { Promise } */ async getCPUUsage(options={}) { const that = this; let { cpuUsageMS, percentage } = options; cpuUsageMS = cpuUsageMS || that.cpuUsageMSDefault; const t1 = that._getCPUInfo(); // t1 時間點 CPU 信息 await sleep(cpuUsageMS); const t2 = that._getCPUInfo(); // t2 時間點 CPU 信息 const idle = t2.idle - t1.idle; const total = t2.total - t1.total; let usage = 1 - idle / total; if (percentage) usage = (usage * 100.0).toFixed(2) + "%"; return usage; } /** * 獲取 CPU 瞬時時間信息 * @returns { Object } CPU 信息 * user <number> CPU 在用戶模式下花費的毫秒數。 * nice <number> CPU 在良好模式下花費的毫秒數。 * sys <number> CPU 在系統模式下花費的毫秒數。 * idle <number> CPU 在空閑模式下花費的毫秒數。 * irq <number> CPU 在中斷請求模式下花費的毫秒數。 */ _getCPUInfo() { const cpus = os.cpus(); let user = 0, nice = 0, sys = 0, idle = 0, irq = 0, total = 0; for (let cpu in cpus) { const times = cpus[cpu].times; user += times.user; nice += times.nice; sys += times.sys; idle += times.idle; irq += times.irq; } total += user + nice + sys + idle + irq; return { user, sys, idle, total, } } } const cpuUsage = new OSUtils().getCPUUsage({ percentage: true }); console.log('cpuUsage: ', cpuUsage.then(data=>console.log(data))); // 我的電腦是6.15%
CPU的負載(loadavg)很好理解,指某段時間內占用 CPU 時間的進程和等待 CPU 時間的進程數為平均負載(load average),這里等待CPU 時間的進程是指等待被喚醒的進程,不包括處于wait狀態進程。
在此之前我們需要學習一個node的API
os.loadavg()
返回包含 1、5 和 15 分鐘平均負載的數組。
平均負載是操作系統計算的系統活動量度,并表示為小數。
平均負載是 Unix 特有的概念。 在 Windows 上,返回值始終為 [0, 0, 0]
它用來描述操作系統當前的繁忙程度,可以簡單地理解為CPU在單位時間內正在使用和等待使用CPU的平均任務數。CPU load過高,說明進程數量過多,在Node中可能體現在用紫禁城模塊反復啟動新的進程。
const os = require('os'); // CPU線程數 const length = os.cpus().length; // 單核CPU的平均負載,返回一個包含 1、5、15 分鐘平均負載的數組 os.loadavg().map(load => load / length);
我們先解釋一個API,要么你看不懂我們獲取內存指標的代碼
該函數返回4個參數,含義及差別如下:
rss: (Resident Set Size)操作系統分配給進程的總的內存大小。包括所有 C++ 和 JavaScript 對象和代碼。(比如,堆棧和代碼段)
heapTotal:堆的總大小,包括3個部分,
已分配的內存,用于對象的創建和存儲,對應于heapUsed
未分配的但可用于分配的內存
未分配的但不能分配的內存,例如在垃圾收集(GC)之前對象之間的內存碎片
heapUsed: 已分配的內存,即堆中所有對象的總大小,是heapTotal的子集。
external: 進程使用到的系統鏈接庫所占用的內存, 比如buffer就是屬于external里的數據。buffer數據不同于其他對象,它不經過V8的內存分配機制,所以也不會有堆內存大小限制。
用如下代碼,打印一個子進程的內存使用情況,可以看出rss大致等于top命令的RES。另外,主進程的內存只有33M比子進程的內存還小,可見它們的內存占用情況是獨立統計的。
var showMem = function(){ var mem = process.memoryUsage(); var format = function(bytes){ return (bytes / 1024 / 1024).toFixed(2) + ' MB'; }; console.log('Process: heapTotal ' + format(mem.heapTotal) + ' heapUsed ' + format(mem.heapUsed) + ' rss ' + format(mem.rss) + ' external:' + format(mem.external)); console.log('-----------------------------------------------------------'); };
對于Node而言,一旦出現內存泄漏,不是那么容易排查。如果監控到內存只升不降,那么鐵定存在內存泄露問題。健康的內存使用應該有升有降。訪問大的時候上升,訪問回落下降
const os = require('os'); // 查看當前 Node 進程內存使用情況 const { rss, heapUsed, heapTotal } = process.memoryUsage(); // 獲取系統空閑內存 const systemFree = os.freemem(); // 獲取系統總內存 const systemTotal = os.totalmem(); module.exports = { memory: () => { return { system: 1 - systemFree / systemTotal, // 系統內存占用率 heap: heapUsed / headTotal, // 當前 Node 進程內存占用率 node: rss / systemTotal, // 當前 Node 進程內存占用系統內存的比例 } } }
磁盤監控主要是監控磁盤的用量。由于日志頻繁寫的緣故,磁盤空間被漸漸用光。一旦磁盤不夠用,將會引發系統的各種問題。給磁盤的使用量設置一個上限,一旦磁盤用量超過警戒值,服務器的管理者就應該整理日志或者清理磁盤。
以下代碼參考easy monitor3.0
先用df -P獲得所有磁盤情況,這個-P是為了防止有換行
startsWith('/')保證是真實磁盤,不是虛擬的
line.match(/(\d+)%\s+(/.*$)/) => 匹配磁盤情況和掛載的磁盤,比如'1% /System/Volumes/Preboot'
match[1]是字符串,表示使用率, match[2]表示掛載的磁盤名稱
const { execSync } = require('child_process'); const result = execSync('df -P', { encoding: 'utf8'}) const lines = result.split('\n'); const metric = {}; lines.forEach(line => { if (line.startsWith('/')) { const match = line.match(/(\d+)%\s+(\/.*$)/); if (match) { const rate = parseInt(match[1] || 0); const mounted = match[2]; if (!mounted.startsWith('/Volumes/') && !mounted.startsWith('/private/')) { metric[mounted] = rate; } } } }); console.log(metric)
I/O負載指的主要是磁盤I/O。反應的是磁盤上的讀寫情況,對于Node編寫的應用,主要是面向網絡服務,是不太可能出現I/O負載過高的情況,多讀書的I/O的壓力來源于數據庫。
獲取I/O指標,我們要了解一個linux命令,叫iostat,如果沒有安裝,需要安裝一下,我們看一下這個命令為啥能反應I/O指標
iostat -dx
屬性說明
rrqm/s: 每秒進行 merge 的讀操作數目。即 rmerge/s(每秒對該設備的讀請求被合并次數,文件系統會對讀取同塊(block)的請求進行合并) wrqm/s: 每秒進行 merge 的寫操作數目。即 wmerge/s(每秒對該設備的寫請求被合并次數) r/s: 每秒完成的讀 I/O 設備次數。即 rio/s w/s: 每秒完成的寫 I/O 設備次數。即 wio/s rsec/s: 每秒讀扇區數。即 rsect/s wsec/s: 每秒寫扇區數。即 wsect/s rkB/s: 每秒讀K字節數。是 rsect/s 的一半,因為每扇區大小為512字節。 wkB/s: 每秒寫K字節數。是 wsect/s 的一半。 avgrq-sz: 平均每次設備I/O操作的數據大小 (扇區)。 avgqu-sz: 平均I/O隊列長度。 await: 平均每次設備I/O操作的等待時間 (毫秒)。 svctm: 平均每次設備I/O操作的處理時間 (毫秒)。 %util: 一秒中有百分之多少的時間用于 I/O 操作,即被io消耗的cpu百分比
我們只監控%util就行
如果 %util 接近 100% ,說明產生的I/O請求太多,I/O系統已經滿負荷,該磁盤可能存在瓶頸。
如果 await 遠大于 svctm,說明 I/O 隊列太長,應用得到的響應時間變慢,如果響應時間超過了用戶可以容許的范圍,這時可以考慮更換更快的磁盤,調整內核 elevator 算法,優化應用,或者升級 CPU。
監控Nodejs的頁面響應時間, 方案選自廖雪峰老師的博客文章。
最近想監控一下Nodejs的性能。記錄分析Log太麻煩,最簡單的方式是記錄每個HTTP請求的處理時間,直接在HTTP Response Header中返回。
記錄HTTP請求的時間很簡單,就是收到請求記一個時間戳,響應請求的時候再記一個時間戳,兩個時間戳之差就是處理時間。
但是,res.send()代碼遍布各個js文件,總不能把每個URL處理函數都改一遍吧。
正確的思路是用middleware實現。但是Nodejs沒有任何攔截res.send()的方法,怎么破?
其實只要稍微轉換一下思路,放棄傳統的OOP方式,以函數對象看待res.send(),我們就可以先保存原始的處理函數res.send,再用自己的處理函數替換res.send:
app.use(function (req, res, next) { // 記錄start time: var exec_start_at = Date.now(); // 保存原始處理函數: var _send = res.send; // 綁定我們自己的處理函數: res.send = function () { // 發送Header: res.set('X-Execution-Time', String(Date.now() - exec_start_at)); // 調用原始處理函數: return _send.apply(res, arguments); }; next(); });
只用了幾行代碼,就把時間戳搞定了。
對于res.render()方法不需要處理,因為res.render()內部調用了res.send()。
調用apply()函數時,傳入res對象很重要,否則原始的處理函數的this指向undefined直接導致出錯。
實測首頁響應時間9毫秒
名詞解釋:
QPS:Queries Per Second意思是“每秒查詢率”,是一臺服務器每秒能夠響應的查詢次數,是對一個特定的查詢服務器在規定時間內所處理流量多少的衡量標準。
互聯網中,作為域名系統服務器的機器的性能經常用每秒查詢率來衡量。
TPS:是TransactionsPerSecond的縮寫,也就是事務數/秒。它是軟件測試結果的測量單位。一個事務是指一個客戶機向服務器發送請求然后服務器做出反應的過程。客戶機在發送請求時開始計時,收到服務器響應后結束計時,以此來計算使用的時間和完成的事務個數。
QPS vs TPS:QPS基本類似于TPS,但是不同的是,對于一個頁面的一次訪問,形成一個TPS;但一次頁面請求,可能產生多次對服務器的請求,服務器對這些請求,就可計入“QPS”之中。如,訪問一個頁面會請求服務器2次,一次訪問,產生一個“T”,產生2個“Q”。
響應時間:執行一個請求從開始到最后收到響應數據所花費的總體時間,即從客戶端發起請求到收到服務器響應結果的時間。
響應時間RT(Response-time),是一個系統最重要的指標之一,它的數值大小直接反應了系統的快慢。
并發數是指系統同時能處理的請求數量,這個也是反應了系統的負載能力。
系統的吞吐量(承壓能力)與request對CPU的消耗、外部接口、IO等等緊密關聯。單個request 對CPU消耗越高,外部系統接口、IO速度越慢,系統吞吐能力越低,反之越高。
系統吞吐量幾個重要參數:QPS(TPS)、并發數、響應時間。
QPS(TPS):(Query Per Second)每秒鐘request/事務 數量
并發數: 系統同時處理的request/事務數
響應時間: 一般取平均響應時間
理解了上面三個要素的意義之后,就能推算出它們之間的關系:
QPS(TPS)= 并發數/平均響應時間
并發數 = QPS*平均響應時間
我們通過一個實例來把上面幾個概念串起來理解。按二八定律來看,如果每天 80% 的訪問集中在 20% 的時間里,這 20% 時間就叫做峰值時間。
公式:( 總PV數 * 80% ) / ( 每天秒數 * 20% ) = 峰值時間每秒請求數(QPS)
機器:峰值時間每秒QPS / 單臺機器的QPS = 需要的機器
1、每天300w PV 的在單臺機器上,這臺機器需要多少QPS?
( 3000000 * 0.8 ) / (86400 * 0.2 ) = 139 (QPS)
2、如果一臺機器的QPS是58,需要幾臺機器來支持?
139 / 58 = 3
到這里,以后如果你做一般中小項目的前端架構,在部署自己的node服務,就知道需要多少機器組成集群來匯報ppt了吧,哈哈,有pv就能推算一個初略值。
我們需要了解一下壓力測試(我們要靠壓測獲取qps),以ab命令為例:
命令格式:
ab [options] [http://]hostname[:port]/path
常用參數如下:
-n requests 總請求數 -c concurrency 并發數 -t timelimit 測試所進行的最大秒數, 可以當做請求的超時時間 -p postfile 包含了需要POST的數據的文件 -T content-type POST數據所使用的Content-type頭信息復制代碼
更多參數請查看官方文檔。
http://httpd.apache.org/docs/2.2/programs/ab.html
例如測試某個GET請求接口:
ab -n 10000 -c 100 -t 10 "http://127.0.0.1:8080/api/v1/posts?size=10"
得到一下數據:
我們從中獲取幾個關鍵指標:
吞吐率(Requests per second)在圖上有顯示
服務器并發處理能力的量化描述,單位是reqs/s,指的是在某個并發用戶數下單位時間內處理的請求數。某個并發用戶數下單位時間內能處理的最大請求數,稱之為最大吞吐率。
記住:吞吐率是基于并發用戶數的。這句話代表了兩個含義:
a、吞吐率和并發用戶數相關
b、不同的并發用戶數下,吞吐率一般是不同的
計算公式:
總請求數/處理完成這些請求數所花費的時間
必須要說明的是,這個數值表示當前機器的整體性能,值越大越好。
2、QPS每秒查詢率(Query Per Second)
每秒查詢率QPS是對一個特定的查詢服務器在規定時間內所處理流量多少的衡量標準,在因特網上,作為域名系統服務器的機器的性能經常用每秒查詢率來衡量,即每秒的響應請求數,也即是最大吞吐能力。
計算公式
QPS(TPS)= 并發數/平均響應時間(Time per request)
在上圖里有Time per request的值,然后我們也有并發數數據,就可以計算出QPS。
這個QPS是壓測數據,真實的qps,可使用日志監控來獲取。
通常情況下,隨著系統的運行,我們的后臺服務會產生各種日志,應用程序會產生應用程序的訪問日志、錯誤日志,運行日志,網絡日志,我們需要一個展示平臺去展示這些日志。
后端一般都用比如ELk去展示,我們前端都是ui老手了,自己可以畫定制的UI界面,不多說了,主要是日志本身要打印符合一定的規范,這樣格式化的數據更利于分析和展示。
并且業務邏輯型的監控主要體現在日志上。通過監控異常日志文件的變動,將新增的異常按異常類型和數量反映出來。某些異常與具體的某個子系統相關,監控出現的某個異常也能反映出子系統的狀態。
在體制監控里也能體現出實際業務的QPS值。觀察QPS的表現能夠檢查業務在時間上的分部。
此外,從訪問日志中也能實現PV和UV的監控。并且可以從中分析出使用者的習慣,預知訪問高峰。
這個也可以通過訪問日志來獲取,并且真實響應時間是需要在controller上打log的。
監控進程一般是檢查操作系統中運行的應用進程數,比如對于采用多進程架構的node應用,就需要檢查工作進程的數量,如果低于預期值,就當發出報警。
查看進程數在linux下很簡單,
假如我們通過Node 提供 child_process 模塊來實現多核 CPU 的利用。child_process.fork() 函數來實現進程的復制。
worker.js 代碼如下:
var http = require('http')\ http.createServer(function(req, res) {\ res.writeHead(200, { 'Content-Type': 'text/plain' })\ res.end('Hello World\n')\ }).listen(Math.round((1 + Math.random()) * 1000), '127.0.0.1')\
通過 node worker.js
啟動它,會監聽 1000 到 2000 之間的一個隨機端口。
master.js 代碼如下:
var fork = require('child_process').fork var cpus = require('os').cpus() for (var i = 0; i < cpus.length; i++) { fork('./worker.js') }
查看進程數的 命令如下:
ps aux | grep worker.js
$ ps aux | grep worker.js lizhen 1475 0.0 0.0 2432768 600 s003 S+ 3:27AM 0:00.00 grep worker.js\ lizhen 1440 0.0 0.2 3022452 12680 s003 S 3:25AM 0:00.14 /usr/local/bin/node ./worker.js\ lizhen 1439 0.0 0.2 3023476 12716 s003 S 3:25AM 0:00.14 /usr/local/bin/node ./worker.js\ lizhen 1438 0.0 0.2 3022452 12704 s003 S 3:25AM 0:00.14 /usr/local/bin/node ./worker.js\ lizhen 1437 0.0 0.2 3031668 12696 s003 S 3:25AM 0:00.15 /usr/local/bin/node ./worker.js\
到此,關于“如何獲取Node性能監控指標”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。