<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Node.js 環(huán)境性能監(jiān)控探究

          共 7113字,需瀏覽 15分鐘

           ·

          2020-09-05 12:02

          作者:@LucasTwilight
          https://juejin.im/post/5c71324b6fb9a049d37fbb7c

          隨著Node v11.0 release版本的發(fā)布,Node已經(jīng)走過了很多年。基于Node產(chǎn)生了很多服務(wù)端框架,來幫助我們獨(dú)立于后端進(jìn)行前端工程的開發(fā)和部署。

          業(yè)務(wù)邏輯的遷移,以及各種MV*框架的服務(wù)端渲染模型的出現(xiàn),讓基于Node的前端SSR策略更依賴服務(wù)器性能。首屏直出性能以及Node服務(wù)的穩(wěn)定性,直接關(guān)系影響著用戶體驗(yàn)。Node作為服務(wù)端語言,相比于Java和PHP這種老服務(wù)端語言來說,對于整體性能的調(diào)控還是不夠完善。雖然有sentry這種報(bào)警平臺來及時通知發(fā)生的錯誤,但是不能夠預(yù)防錯誤的發(fā)生。如何防患于未然,首先需要理解Node.js性能監(jiān)控的主要指標(biāo)。

          下面的代碼均是基于Egg框架的,如果對Egg不熟悉的小伙伴可以先去瀏覽一下文檔

          指標(biāo)

          服務(wù)器的資源瓶頸主要有下面幾個:

          1. CPU
          2. 內(nèi)存
          3. 磁盤
          4. I/O
          5. 網(wǎng)絡(luò)

          考慮到不同的Node環(huán)境,其對于資源的需求類型也是不盡相同的。如果Node只是用于前端SSR的話,那么CPU和網(wǎng)絡(luò)就會成為主要的性能瓶頸。

          當(dāng)然如果你需要使用Node來進(jìn)行數(shù)據(jù)持久化相關(guān)的工作,那么I/O和磁盤也會有很高的占用率。

          即使是前端發(fā)展非常超前的公司,也很少會用Node作為業(yè)務(wù)數(shù)據(jù)的支撐。充其量當(dāng)做BFF層來為前端提供數(shù)據(jù)服務(wù),并不直接接觸持久化的數(shù)據(jù)。所以磁盤和I/O很難成為當(dāng)下前端性能的瓶頸。

          即使存在使用Node進(jìn)行數(shù)據(jù)持久化平臺,大多數(shù)也是實(shí)驗(yàn)性質(zhì)的平臺或者是內(nèi)部平臺。不直接面向業(yè)務(wù)場景。

          所以,在大多數(shù)場景下,CPU、內(nèi)存以及網(wǎng)絡(luò)就可以說是Node的主要性能瓶頸。

          CPU指標(biāo)

          CPU負(fù)載和CPU使用率

          顧名思義,這兩個指標(biāo)都是用來評估系統(tǒng)當(dāng)前CPU的繁忙程度的量化指標(biāo)。CPU負(fù)載和CPU使用率是從兩個不同的角度來量化CPU的繁忙程度的。

          • CPU負(fù)載:進(jìn)程角度
          • CPU使用率:CPU時間分配

          進(jìn)程是資源分配的最小單位。

          這句話在操作系統(tǒng)的教科書上或者各位的考試卷上都多多少少出現(xiàn)過。也就是,系統(tǒng)按照進(jìn)程級別來進(jìn)行資源的分配,一個CPU核心在一個時刻只能夠?yàn)?個進(jìn)程提供服務(wù)。

          那么, CPU的負(fù)載也就很好理解了。在某個時間段內(nèi),占用以及等待CPU的進(jìn)程總數(shù)就是CPU在這個時間段內(nèi)的負(fù)載(load average),在大多數(shù)情況下,我們稱這個標(biāo)準(zhǔn)為loadavg。

          而CPU利用率(cpu utilization),則是量化CPU時間占用狀況的,一般我們認(rèn)為CPU利用率 = 1 - 空閑CPU時間(idle time) / CPU總時間。

          wiki上已經(jīng)解釋的非常清楚了,請自備梯子(https://en.wikipedia.org/wiki/Load_(computing)#CPU_load_vs_CPU_utilization)

          量化CPU指標(biāo)

          那么這兩個指標(biāo)到底哪個才最能代表的系統(tǒng)的實(shí)際狀態(tài)呢?

          滑梯:CPU

          人:進(jìn)程

          假如有4個滑梯。每個滑梯上最多可以塞得下10個人。我們假設(shè)所有的人的大小一致。那么,可以得到如下的類比:

          • Loadavg = 0,表示滑梯上一個人都沒有
          • Loadavg = 0.5, 表示平均每個滑梯上的人都占了滑梯的一半,也就是總共20個人在滑梯上,由于CPU調(diào)度策略,這些人一般會均勻分配(每個人都會挑人少的滑梯)
          • Loadavg = 1,表示每個滑梯上都塞滿人了,沒有任何空閑空間
          • Loadavg = 2, 表示不僅僅每個滑梯上都塞滿了人,還有40個人在后面等著

          以上的類比都是基于瞬時的loadavg得到的。

          一般對于loadavg的量化,我們都是采用3個不同的時間標(biāo)準(zhǔn)來進(jìn)行的。1分鐘,5分鐘以及15分鐘。

          1分鐘的指標(biāo)是很難得到較為均衡的指標(biāo)的。因?yàn)?分鐘時間太短,可能某一秒的峰值就能夠影響到1分鐘時間段內(nèi)的平均指標(biāo)。但是,1分鐘內(nèi),如果loadavg突然達(dá)到很高的值,也可能是系統(tǒng)崩潰的前兆,也是需要警惕的一個指標(biāo)。

          而5分鐘和15分鐘則是較為合適的評判指標(biāo)。當(dāng)CPU在5分鐘或者15分鐘內(nèi)都保持高負(fù)荷運(yùn)作,對于整個系統(tǒng)是非常危險(xiǎn)的。遇到過堵車的人都應(yīng)該知道,一旦發(fā)生了堵車,只要堵塞不及時清理,就會越堵越長。CPU也是這樣,如果CPU上等待的進(jìn)程阻塞的較多,那么后面進(jìn)入隊(duì)列的任務(wù)就更加搶占不到資源,也就會被一直阻塞了。

          在MAC上可以在root權(quán)限下,使用sysctl -n vm.loadavg來獲得。

          // /app/lib/cpu.js
          const os = require('os');
          // cpu核心數(shù)
          const length = os.cpus().length;
          // 單核CPU的平均負(fù)載
          os.loadavg().map(load => load / length);

          而CPU利用率則是不太好作為直接評判標(biāo)準(zhǔn)的數(shù)值。由于進(jìn)程阻塞在CPU上的原因不相同,對于CPU密集型任務(wù)來說,CPU利用率可以很好地表示當(dāng)前CPU的工作情況,但是對于I/O密集型的任務(wù)來說,CPU空閑不代表CPU無事可做,可能是任務(wù)被掛起,去進(jìn)行其他操作了。

          但是,對于進(jìn)行SSR的Node系統(tǒng)來說,渲染基本上可以理解為CPU密集型業(yè)務(wù),所以這個指標(biāo)在一定程度上可以體現(xiàn)出當(dāng)前業(yè)務(wù)環(huán)境的CPU性能。

          // /app/lib/cpu.js

          const os = require('os');
          // 獲取當(dāng)前的瞬時CPU時間
          const instantaneousCpuTime = () => {
          let idleCpu = 0;
          let tickCpu = 0;
          const cpus = os.cpus();
          const length = cpus.length;

          let i = 0;
          while(i < length) {
          let cpu = cpus[i];

          for (let type in cpu.times) {
          tickCpu += cpu.times[type];
          }

          idleCpu += cpu.times.idle;
          i++;
          }

          const time = {
          idle: idleCpu / cpus.length, // 單核CPU的空閑時間
          tick: tickCpu / cpus.length, // 單核CPU的總時間
          };
          return time;
          }
          const cpuMetrics = () => {
          const startQuantize = instantaneousCpuTime();
          return new Promise((resolve, reject) => {
          setTimeout(() => {
          const endQuantize = instantaneousCpuTime();
          const idleDifference = endQuantize.idle - startQuantize.idle;
          const tickDifference = endQuantize.tick - startQuantize.tick;

          resolve(1 - (idleDifference / tickDifference));
          }, 1000);
          });
          };

          cpuMetrics().then(res => {
          console.log(res);
          // 0.074999
          });

          結(jié)合上述兩個指標(biāo),可以大致得到系統(tǒng)的運(yùn)行狀態(tài),從而對于系統(tǒng)進(jìn)行干預(yù)。比如將SSR降級為CSR。

          內(nèi)存指標(biāo)

          內(nèi)存是一個非常容易量化的指標(biāo)。內(nèi)存占用率是評判一個系統(tǒng)的內(nèi)存瓶頸的常見指標(biāo)。對于Node來說,內(nèi)部內(nèi)存堆棧的使用狀態(tài)也是一個可以量化的指標(biāo)。

          // /app/lib/memory.js
          const os = require('os');
          // 獲取當(dāng)前Node內(nèi)存堆棧情況
          const { rss, heapUsed, heapTotal } = process.memoryUsage();
          // 獲取系統(tǒng)空閑內(nèi)存
          const sysFree = os.freemem();
          // 獲取系統(tǒng)總內(nèi)存
          const sysTotal = os.totalmem();

          module.exports = {
          memory: () => {
          return {
          sys: 1 - sysFree / sysTotal, // 系統(tǒng)內(nèi)存占用率
          heap: heapUsed / headTotal, // Node堆內(nèi)存占用率
          node: rss / sysTotal, // Node占用系統(tǒng)內(nèi)存的比例
          }
          }
          }

          對于process.memoryUsage()拿到的值有一些需要關(guān)注的地方:

          我的Node啟蒙書《深入淺出Node.js》這本書,雖然版本已經(jīng)落后了現(xiàn)在的Node.js很多release了,但是其中講到的關(guān)于V8引擎的GC機(jī)制的內(nèi)容,仍然非常受用,推薦大家買正版支持一下樸靈老師。

          • rss:表示node進(jìn)程占用的內(nèi)存總量。
          • heapTotal:表示堆內(nèi)存的總量。
          • heapUsed:實(shí)際堆內(nèi)存的使用量。
          • external:外部程序的內(nèi)存使用量,包含Node核心的C++程序的內(nèi)存使用量。

          首先需要關(guān)注的是內(nèi)存堆棧,也就是堆內(nèi)存的占用。在Node的單線程模式下,C++程序(V8引擎)會為Node申請一定的內(nèi)存,來作為Node線程的內(nèi)存資源heapTotal。而在我們Node的使用過程中,聲明的新的變量都會使用這些內(nèi)存來進(jìn)行存儲heapUsed。

          Node的分代式GC算法會在一定程度上浪費(fèi)部分內(nèi)存資源,所以當(dāng)heapUsed達(dá)到heapTotal一半的時候,就可以強(qiáng)制觸發(fā)GC操作了global.gc()。gc操作相關(guān)可以看下這篇文章。對于系統(tǒng)內(nèi)存的監(jiān)控處理,不能夠僅僅像Node內(nèi)存級別一樣,進(jìn)行GC操作就可以,而同樣需要進(jìn)行渲染降級。70% ~ 80%的內(nèi)存占用就是非常危險(xiǎn)的情況了。具體的數(shù)值需要根據(jù)環(huán)境所在的宿主機(jī)來確定。

          具體和Node內(nèi)存GC策略以及分配規(guī)則相關(guān)的,可以看StrongLoop - Node.js Performance Tip of the Week: Managing Garbage Collection。

          QPS

          嚴(yán)格意義上來說,QPS不能夠作為web監(jiān)控的直接標(biāo)準(zhǔn)。但是當(dāng)服務(wù)器在高負(fù)載的情況下,不能夠得到和壓測情況下接近的QPS的時候,就需要考慮是某些其他原因?qū)е铝朔?wù)器的性能瓶頸。一般在進(jìn)行Node環(huán)境下的SSR的時候,假設(shè)Node-Cluster最大線程數(shù)為10,那么可以并行進(jìn)行10個頁面的渲染,當(dāng)然這也取決于宿主CPU的核心數(shù)。

          在將Node作為SSR的宿主環(huán)境的情況下,可以很容易地記錄到當(dāng)前機(jī)器在一段時間內(nèi)響應(yīng)的請求數(shù)。之前在做畢業(yè)論文的時候,有嘗試過對于web站點(diǎn)進(jìn)行壓力測試的幾種方式。

          ApacheBench

          http_load

          Seige

          這三個web壓測工具大同小異,都能夠進(jìn)行并發(fā)請求測試,對于web站點(diǎn)進(jìn)行多用戶的并發(fā)訪問,并且記錄到所有請求過程的響應(yīng)時間,并且重復(fù)進(jìn)行請求,可以很好地模擬Node環(huán)境在壓力下的表現(xiàn)。

          根據(jù)性能壓測的結(jié)果,以及對于需求的流量峰值的評估,可以大致計(jì)算出需要多少臺機(jī)器才能夠保證web服務(wù)的穩(wěn)定性,保證大多數(shù)用戶能夠在可接受的時間內(nèi)得到響應(yīng)。

          測試

          根據(jù)上述三個指標(biāo),對于本地啟動的環(huán)境進(jìn)行壓測。

          本地啟動的Node環(huán)境是基于Egg框架擴(kuò)展的React SSR環(huán)境,實(shí)際線上環(huán)境由于很多靜態(tài)資源(包括javascript腳本、css、圖片等)都被推到了CDN上,所以這些資源不會直接對環(huán)境產(chǎn)生壓力,而且生產(chǎn)環(huán)境和開發(fā)環(huán)境也存在很多流程上的區(qū)別,所以實(shí)際性能要比本地啟動的好很多。這里為了測試方便,所以直接在本地啟動了Egg工程。

          測試環(huán)境本地可以使用PM2啟動Node工程,或者直接通過Node命令啟動,在本地測試環(huán)境盡量不要使用webpack-dev-server這樣的開發(fā)環(huán)境啟動,這樣可能會導(dǎo)致Node的Cluster模式不能夠很好地運(yùn)行,監(jiān)控線程阻塞掉頁面渲染的線程?;贓gg的環(huán)境可以使用schedule定時任務(wù)來定時打印環(huán)境監(jiān)控日志。具體使用可以看Egg的文檔,里面會寫的比較詳細(xì)。然后自定義一個日志類型,將監(jiān)控日志獨(dú)立于應(yīng)用日志存儲起來,便于分析和可視化。

          // /app/schedule/monitor.js
          const memory = require('../lib/memory');
          const cpu = require('../lib/cpu');

          module.exports = app => {
          return {
          schedule: {
          interval: 10000,
          type: 'worker',
          },
          async task(ctx) {
          ctx.app.getLogger('monitorLogger').info('你想打印的日志結(jié)果')
          }
          }
          }


          // /config/config.prod.js
          const path = require('path');
          // 自定義日志,將日志文件自定義到一個單獨(dú)的監(jiān)控日志文件中
          module.exports = appInfo => {
          return {
          customLogger: {
          monitorLogger: { file: path.resolve(__dirname, '../logs/monitor.log') }
          }
          }
          }

          然后準(zhǔn)備siege進(jìn)行壓測:Mac上安裝siege

          或者在MAC上可以更簡單地使用brew來直接安裝siege。推薦使用這種方法,因?yàn)橹苯酉螺d源碼包編譯的話,可能會發(fā)生libssl庫鏈接不上的問題,導(dǎo)致不能夠進(jìn)行https請求。

          測試和監(jiān)控結(jié)果

          • 在無請求訪問情況下:
          • siege

          配置siege的請求URL列表:我們可以將想要siege請求的URL放在文件里面,通過siege命令進(jìn)行讀取(這里需要注意,siege只能夠訪問http站點(diǎn),如果站點(diǎn)強(qiáng)制https的話可能需要考慮其他方法)。

          • urls文件

          urls執(zhí)行:siege -c 10 -r 5 -f urls -i -b

          -c:模擬有n個用戶同時訪問

          -r:重復(fù)測試n次

          -f:指定測試URL的獲取文件

          -I:指定隨機(jī)訪問URL獲取文件中的URL

          -b:請求無需等待

          上面的siege命令就表示,每次并發(fā)10個,分別請求urls文件中的隨機(jī)一個站點(diǎn),然后這樣的并發(fā)一共執(zhí)行5次,并且無需等待直接訪問。

          可以看到,siege對于服務(wù)端進(jìn)行了515次命中,因?yàn)榉?wù)端除了主頁面還有一些靜態(tài)資源需要請求,這些命中包含頁面,javascript腳本,圖片以及css等,平均每個資源的響應(yīng)時間為0.83秒

          請求結(jié)束時間為20:29:37,可以看到這個時間之后,cpu的各項(xiàng)指標(biāo)都開始下降,而內(nèi)存沒有非常明顯的變化。

          再進(jìn)行一次壓力較大的測試:

          執(zhí)行:siege -c 100 -r 5 -f urls -i -b,將并發(fā)數(shù)增加到10倍也就是100并發(fā)。

          可以看到平均響應(yīng)時間下降到了3.85秒,非常明顯。而且loadavg相比第一次壓測的時候,有著非常明顯的上升。內(nèi)存使用的變化不大,

          因?yàn)闇y試環(huán)境的機(jī)器是虛擬機(jī),不會獨(dú)占物理機(jī)的所有資源,但是獲取的CPU數(shù)卻是物理機(jī)的CPU數(shù)。由于之前我們對于每種參數(shù)都計(jì)算了單核的情況,所以這里和CPU相關(guān)的結(jié)果需要和物理機(jī)核心數(shù)以及虛擬機(jī)占用的核心數(shù)相關(guān)。

          有興趣的小伙伴可以嘗試一下機(jī)器的極限ORZ?;蛘咴谖锢頇C(jī)上嘗試一下壓測。我沒有敢這么傷害我的小兄弟。

          Conclusion

          現(xiàn)在很多業(yè)務(wù)開始往前端進(jìn)行遷移,BFF(backends for frontends)的概念有很多團(tuán)隊(duì)已經(jīng)開始逐漸嘗試去做了。讓后端專注于提供統(tǒng)一的數(shù)據(jù)模型,然后將業(yè)務(wù)邏輯遷移到基于Node.js的BFF層中,讓前端給自己提供api接口,這樣就剩下了很多前后端聯(lián)調(diào)的成本,讓后端提供的RPC或者HTTP接口更加通用,更少地修改后端工程,加快開發(fā)的效率。

          但是這樣就非常依賴Node端的穩(wěn)定性,在BFF架構(gòu)中,一旦Node端發(fā)生錯誤導(dǎo)致阻塞,則所有前端頁面都會丟失服務(wù),造成很嚴(yán)重的后果,所以Node端的監(jiān)控越來越有意義。結(jié)合一些傳統(tǒng)平臺比如sentry或者zabbix可以幫助構(gòu)建一個穩(wěn)定的前端部署環(huán)境。

          ??愛心三連擊

          1.看到這里了就點(diǎn)個在看支持下吧,你的在看是我創(chuàng)作的動力。

          2.關(guān)注公眾號程序員成長指北,回復(fù)「1」加入Node進(jìn)階交流群!「在這里有好多 Node 開發(fā)者,會討論 Node 知識,互相學(xué)習(xí)」!

          3.也可添加微信【ikoala520】,一起成長。


          “在看轉(zhuǎn)發(fā)”是最大的支持







          瀏覽 57
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  韩国女主播操逼 | XA片一级日本 | 中国A毛片 | 韩国一区免费看 | 亚洲性爱在线 |