애플리케이션이 원활하게 실행되고 있지만 갑자기 CPU 사용량이 95% 또는 100%로 급증하는 높은 부하를 발견한다고 상상해 보세요. 이는 종종 Node.js 애플리케이션의 CPU 바운드 작업을 나타냅니다.
CPU 바운드 작업에는 상당한 처리 능력이 필요하며 I/O 작업과 같은 다른 리소스로 쉽게 전환할 수 없습니다. 이러한 작업에는 집약적인 계산, 이미지/비디오 처리, 암호화 작업 및 머신 러닝 추론 등이 포함됩니다.
원인 코드를 찾아 높은 CPU 사용량을 수정하려면 애플리케이션을 프로파일링해야 합니다. 이 가이드에서는 Node.js 애플리케이션을 프로파일링하기 위한 몇 가지 도구와 기술을 살펴봅니다.
시작하겠습니다!
시작하기 전에 다음 사항을 확인하세요.
애플리케이션을 프로파일링하는 방법을 보여주기 위해 Fastify 서버를 사용하는 Node.js 프로젝트로 작업합니다. 이 서버에는 사용자가 암호를 제출하여 가입할 수 있는 엔드포인트가 포함되어 있습니다. 서버는 안전한 저장을 위해 임의로 생성된 솔트를 이용하여 암호를 해시합니다. 해싱 프로세스는 CPU를 사용하므로 CPU 사용량이 높은 경우가 많습니다.
다음 명령을 사용하여 다음 리포지토리를 컴퓨터에 복제하여 시작합니다.
git clone https://github.com/betterstack-community/nodejs-profiler-demo.git
그런 다음, 새로 생성된 디렉터리로 이동합니다.
cd nodejs-profiling-demo
다음 명령을 실행하여 Fastify(웹 프레임워크) 및 Autocannon(부하 테스트 도구)을 포함한 의존성을 설치합니다.
npm install
의존성을 설치한 후 개발 서버를 시작합니다.
node index.js
다음 출력이 표시됩니다.
server listening on 3000
다른 터미널을 열어 서버를 테스트합니다.
curl -X POST -H "Content-Type: application/json" -d '{"password":"userPassword123"}' http://localhost:3000/register
명령을 실행하면 다음과 유사하지만 값이 다른 응답을 받게 됩니다.
Output
{"salt":"c2f1e60a8d47afed730eb7f3d84b7e39","hashedPassword":"4f9f64c8c631418c46392faa46f4ea2f247e1216fbec302ca49af3add4d5e9428f7ae9e07c716038082e14aaf7d01eac62f0d2820eabf79e398426720c819e13"}
암호는 데이터베이스에 직접 저장되는 대신 안전한 저장을 위해 해시 처리되며 애플리케이션은 단지 이를 기록하기만 합니다.
해싱 프로세스는 index.js
파일, 특히 /register
엔드포인트 내에서 관찰할 수 있습니다.
fastify.post("/register", (request, reply) => {
const { password } = request.body;
if (!password) {
return reply.status(400).send({ error: "Password is required" });
}
const salt = crypto.randomBytes(16).toString("hex");
const hashedPassword = crypto
.pbkdf2Sync(password, salt, 100000, 64, "sha512")
.toString("hex");
reply.send({ salt, hashedPassword });
});
하지만 crypto.pbkdf2Sync
를 사용한 동기식 해시 작업은 해싱 프로세스가 완료될 때까지 이벤트 루프를 차단하므로 성능 저하 및 확장성 문제가 발생할 수 있습니다. 따라서 동기화 작업이 완료될 때까지 기다리는 동안 수신 요청이 지연되거나 시간 초과가 발생할 수 있습니다.
결과적으로 동기식 해싱 작업이 완료되기를 기다리는 동안 수신 요청이 지연되거나 시간 초과가 발생할 수 있습니다.
보통 컴퓨터 프로그램의 작업은 I/O 바운드와 CPU 바운드의 두 가지 주요 범주로 분류되곤 합니다.
I/O 바운드 작업에는 파일 I/O, 네트워크 요청 또는 데이터베이스 상호 작용과 같이 일반적으로 운영 체제에서 관리하는 작업이 포함됩니다. Node.js에서는 싱글 스레드 특성으로 인해 이러한 작업은 비동기적으로 처리되기 때문에 이벤트 루프를 차단하지 않습니다. Node.js는 이벤트 기반 아키텍처와 논블로킹 I/O 작업을 활용하여 I/O 작업이 완료될 때까지 기다리는 동안 다른 코드를 계속 실행할 수 있습니다. 이를 통해 여러 I/O 작업을 동시에 효율적으로 관리할 수 있습니다. 예를 들어 API에서 데이터 가져오기, 디스크에서 파일 읽기 또는 데이터베이스 쿼리가 있습니다.
반면, CPU 바운드 작업에는 상당한 처리 능력이 필요하며 CPU를 직접 사용해야 합니다. 이러한 작업은 실행하는 데 시간이 너무 오래 걸리면 이벤트 루프가 차단되어 애플리케이션이 응답하지 않을 수 있습니다. 싱글 스레드 이벤트 루프는 다른 작업을 진행하기 전에 이러한 작업이 완료될 때까지 기다려야 하기 때문에 Node.js에서 CPU 바운드 작업은 특정 문제를 야기합니다. CPU 바운드 작업의 예로는 이미지 처리, 비디오 인코딩 및 복잡한 계산 등이 있습니다.
높은 CPU 사용량 문제를 해결하고 CPU 사용량 프로파일링을 고려할 때 CPU 바운드 작업 때문인 경우가 많습니다.
Node.js에는 실행중인 Node.js 앱을 프로파일링하는 프로파일링 도구가 기본으로 포함되어 있습니다. 이 도구는 애플리케이션의 콜 스택을 정기적으로 샘플링하는 V8 프로파일러를 사용합니다. 각 샘플은 샘플을 채취할 때 스택의 맨 위에 함수를 기록합니다. 이러한 샘플을 분석하여 CPU 사용량 급증이 발생하는 위치를 식별할 수 있습니다.
애플리케이션을 프로파일링하려면 다음과 같이 --prof
플래그를 node
명령에 전달합니다.
node --prof index.js
다음으로, 보다 의미 있는 CPU 프로파일링을 위해 애플리케이션에 과도한 부하를 주어야 합니다. Autocannon과 같은 부하 테스트 도구를 사용하여 이를 수행할 수 있습니다.
두 번째 터미널을 열고 다음 명령을 사용하여 11초 동안 애플리케이션을 로드 테스트합니다.
npx autocannon --renderStatusCodes -d 11 -m POST -H "Content-Type: application/json" -b '{"password":"userPassword123"}' http://localhost:3000/register
부하 테스트가 완료되면 다음과 같은 출력이 표시됩니다.
Running 11s test @ http://localhost:3000/register
10 connections
┌─────────┬────────┬────────┬─────────┬─────────┬────────────┬───────────┬─────────┐
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
├─────────┼────────┼────────┼─────────┼─────────┼────────────┼───────────┼─────────┤
│ Latency │ 217 ms │ 991 ms │ 3683 ms │ 5830 ms │ 1068.69 ms │ 767.62 ms │ 5830 ms │
└─────────┴────────┴────────┴─────────┴─────────┴────────────┴───────────┴─────────┘
┌───────────┬────────┬────────┬─────────┬─────────┬─────────┬───────┬────────┐
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
├───────────┼────────┼────────┼─────────┼─────────┼─────────┼───────┼────────┤
│ Req/Sec │ 8 │ 8 │ 9 │ 10 │ 8.91 │ 0.67 │ 8 │
├───────────┼────────┼────────┼─────────┼─────────┼─────────┼───────┼────────┤
│ Bytes/Sec │ 2.9 kB │ 2.9 kB │ 3.27 kB │ 3.63 kB │ 3.23 kB │ 242 B │ 2.9 kB │
└───────────┴────────┴────────┴─────────┴─────────┴─────────┴───────┴────────┘
┌──────┬───────┐
│ Code │ Count │
├──────┼───────┤
│ 200 │ 98 │
└──────┴───────┘
Req/Bytes counts sampled once per second.
# of samples: 11
108 requests in 11.07s, 35.6 kB read
부하 테스트를 완료한 후, 첫 번째 터미널에서 CTRL + C
를 사용하여 서버를 중지하면 프로파일러 출력이 현재 디렉토리에 있는 파일에 기록됩니다. 파일 이름은 isolate-0x<number>-v8.log
이며, 여기서 0x<number>-v8
은 16진수 문자열을 나타냅니다.
다음 명령을 사용하여 디렉토리 내용을 나열합니다.
ls -l
출력은 다음과 같이 파일이 표시됩니다.
total 7264
-rw-rw-r-- 1 stanley stanley 623 Jun 4 11:35 index.js
-rw-rw-r-- 1 stanley stanley 7399577 Jun 4 11:39 isolate-0x650d000-56183-v8.log
...
isolate-0x<number>-v8.log
파일에는 쉽게 읽을 수 없는 원시 데이터가 들어 있습니다. 사람이 읽을 수 있도록 하려면 --prof-process
플래그와 함께 다음 명령을 실행합니다.
node --prof-process isolate-0x<number>-v8.log > profile.txt
원하는 텍스트 편집기로 profile.txt
를 엽니다.
code profile.txt
파일 내의 [Summary]
섹션으로 이동합니다.
profile.txt
[Summary]:
ticks total nonlib name
23 0.2% 100.0% JavaScript
0 0.0% 0.0% C++
50 0.4% 217.4% GC
11674 99.8% Shared libraries
Node.js는 자바스크립트 파일을 실행하고 일부 C++ 코드 및 기타 라이브러리를 실행합니다. 요약 출력은 대부분의 틱이 자바스크립트에서 발생했음을 나타내며, 이는 자바스크립트 코드에 주의를 기울여야 함을 시사합니다.
파일의 [JavaScript] 섹션으로 이동하여 어떤 함수가 가장 많은 CPU 시간을 소비하는지 확인합니다.
[JavaScript]:
ticks total nonlib name
2 0.0% 8.7% JS: ^processTimers node:internal/timers:499:25
2 0.0% 8.7% JS: +wrappedFn node:internal/errors:535:21
2 0.0% 8.7% JS: *normalizeString node:path:66:25
1 0.0% 4.3% RegExp: ^((?:@[^/\\%]+\/)?[^./\\%][^/\\%]*)(\/.*)?$
1 0.0% 4.3% JS: ^toRealPath node:internal/modules/helpers:49:20
1 0.0% 4.3% JS: ^sendTrailer /home/stanley/nodejs-profiling-demo/node_modules/fastify/lib/reply.js:765:22
1 0.0% 4.3% JS: ^pbkdf2Sync node:internal/crypto/pbkdf2:61:20
...
1 0.0% 4.3% JS: +<anonymous> node:internal/validators:458:42
이 섹션에서는 CPU 시간이 가장 많이 소요되는 함수를 보여 주어 특정 영역을 타겟팅하여 최적화할 수 있습니다. 특히, crypto
모듈의 틱은 암호화 작업이 CPU 사용량의 주요 원인일 수 있음을 시사합니다. 이 인사이트를 통해 코드의 특정 부분을 최적화하는 데 도움을 받을 수 있습니다.
높은 CPU 사용량을 확인하는 보다 신뢰할 수 있는 방법은 콜 스택을 검사하는 것입니다. 프로파일링 출력의 "Bottom up" 섹션으로 이동합니다.
Bottom up (heavy) profile]:
Note: percentage shows a share of a particular caller in the total
amount of its parent calls.
Callers occupying less than 1.0% are not shown.
ticks parent name
10566 90.3% /home/stanley/.nvm/versions/node/v22.2.0/bin/node
9272 87.8% JS: ^pbkdf2Sync node:internal/crypto/pbkdf2:61:20
9272 100.0% JS: ^<anonymous> file:///home/stanley/nodejs-profiling-demo/index.js:6:27
9272 100.0% JS: ^preHandlerCallback /home/stanley/nodejs-profiling-demo/node_modules/fastify/lib/handleRequest.js:126:29
9272 100.0% JS: ^validationCompleted /home/stanley/nodejs-profiling-demo/node_modules/fastify/lib/handleRequest.js:103:30
9272 100.0% JS: ^preValidationCallback /home/stanley/nodejs-profiling-demo/node_modules/fastify/lib/handleRequest.js:83:32
829 7.8% JS: ~pbkdf2Sync node:internal/crypto/pbkdf2:61:20
829 100.0% JS: ~<anonymous> file:///home/stanley/nodejs-profiling-demo/index.js:6:27
829 100.0% JS: ~preHandlerCallback /home/stanley/nodejs-profiling-demo/node_modules/fastify/lib/handleRequest.js:126:29
829 100.0% JS: ~validationCompleted /home/stanley/nodejs-profiling-demo/node_modules/fastify/lib/handleRequest.js:103:30
829 100.0% JS: ~preValidationCallback /home/stanley/nodejs-profiling-demo/node_modules/fastify/lib/handleRequest.js:83:32
이 섹션에서는 pbkdf2Sync
함수가 대부분의 CPU 시간을 소비하고 있음을 추가로 확인합니다.
또 다른 방법은 성능 데이터를 수집하고 CPU 시간을 가장 많이 사용하는 함수에 대한 보고서를 생성할 수 있는 Chrome DevTools를 사용하는 것입니다. 이를 통해 성능 병목 현상을 쉽게 파악할 수 있습니다.
이 프로세스를 시작하려면 --inspect
플래그를 사용하여 서버를 시작하세요.
node --inspect index.js
서버가 가동되고 실행되면 Chrome 또는 Chromium 기반 브라우저를 실행하고 주소 표시줄에 chrome://inspect
를 입력합니다. Node.js 스크립트에 해당하는 inspect 링크를 찾아 클릭합니다.
이 작업을 수행하면 DevTools 창이 열립니다. 그런 다음 "성능" 탭으로 전환하고 아래 스크린샷에 표시된 record 버튼을 클릭합니다.
이제 터미널로 돌아가서 부하 테스트를 다시 실행하십시오.
npx autocannon --renderStatusCodes -d 11 -m POST -H "Content-Type: application/json" -b '{"password":"userPassword123"}' http://localhost:3000/register
부하 테스트를 완료한 후 DevTools 창으로 돌아가서 Stop 을 클릭하여 프로파일링 프로세스를 종료합니다.
중지하면 포괄적인 성능 프로필이 표시됩니다. 처음에는 일련의 상자들로 표시됩니다.
이를 플레임 차트라고 합니다. 시간을 나타내는 가로축과 콜 스택을 나타내는 세로축이 있습니다.
가로축을 확대하여 콜 스택을 자세히 살펴볼 수 있습니다. 가장 쉬운 방법은 가로축의 아무 지점이나 클릭한 상태에서 드래그하여 작은 부분을 선택하는 것입니다.
이를 통해 특정 시간 프레임에 집중하고 함수 호출의 순서와 기간을 더 잘 이해할 수 있습니다.
확대/축소하면 콜 스택이 자세히 표시됩니다. 맨 위에 있는 이벤트는 맨 아래에 있는 이벤트를 유발하는 이벤트입니다. 여기서 DevTools는 Node.js 함수의 실행 경로를 보여줍니다. 이 프로세스는 내부 Node.js 메커니즘 (processTicksAndRejections
, endReadableNT
, emit
)으로 시작하여 사용자 지정 구문 분석 (defaultJsonParser
, onEnd
)을 통해 진행됩니다. 여기에는 다양한 요청 처리 단계 preValidationCallback
, validationCompleted
, preHandlerCallback
, handler
가 포함됩니다. 시퀀스는 익명 함수와 중요한 pbkdf2Sync
호출로 마무리됩니다.
콜백을 상향식으로 보려면 "Bottom-Up" 탭으로 전환하여 작업에 소요된 집계 시간을 확인합니다.
여기에서 99%의 시간이 run
에 소비되는 것을 볼 수 있습니다. run
을 확장하면 pbkdf2Sync
함수가 있는 것을 볼 수 있는데, 이는 대부분의 CPU 시간을 소비한다는 큰 힌트입니다.
이제 콘텐츠를 지울 수 있습니다.
Chrome DevTools를 사용한 프로파일링을 처리합니다. 다음으로 Node Inspector API를 사용합니다.
또 다른 접근 방식은 프로그래밍 방식으로 V8 인스펙터와 상호 작용할 수 있는 Node Inspector API를 사용하는 것입니다. 다음 단계에 따라 Inspector API를 사용하여 애플리케이션을 프로파일링할 수 있습니다.
먼저 루트 디렉터리에 다음 내용의 inspector.js
파일을 만듭니다.
import * as inspector from "node:inspector/promises";
import fs from "node:fs/promises"; // Use promises for cleaner async/await usage
const session = new inspector.Session();
async function enableProfiling() {
try {
await session.connect();
await session.post("Profiler.enable");
} catch (error) {
console.error("Error enabling profiling:", error);
}
}
async function startCpuProfiling() {
try {
await enableProfiling();
await session.post("Profiler.start");
} catch (error) {
console.error("Error starting CPU profiling:", error);
}
}
async function stopCpuProfiling() {
try {
const { profile } = await session.post("Profiler.stop");
await fs.writeFile("./profile.cpuprofile", JSON.stringify(profile));
} catch (error) {
console.error("Error stopping CPU profiling:", error);
} finally {
await session.disconnect();
}
}
process.on("SIGUSR1", startCpuProfiling);
process.on("SIGUSR2", stopCpuProfiling);
이 코드에서는 필요한 모듈을 가져오고 inspector.Session
인스턴스를 생성합니다. enableProfiling
함수는 세션에 연결하고 프로파일러를 활성화합니다.
startCpuProfiling
함수는 enableProfiling
을 호출하고 CPU 프로파일링을 시작합니다. 반대로 stopCpuProfiling
함수는 프로파일링을 중지하고, 프로필 데이터를 파일에 저장한 후, 세션의 연결을 끊어 발생하는 모든 오류를 처리합니다.
스크립트는 SIGUSR1
및 SIGUSR2
시그널을 수신 대기합니다. SIGUSR1
을 수신하면 프로파일링이 시작되고 SIGUSR2
는 프로파일링을 중지하고 결과를 저장하여 Unix 시그널을 통해 프로파일링을 제어할 수 있습니다.
개발 서버를 시작할 때 이 코드가 실행되도록 하려면 index.js
에서 모듈을 가져오세요.
import Fastify from "fastify";
import crypto from "node:crypto";
import "./inspector.js";
이를 통해 플래그 없이 개발 서버를 실행하고 &
를 사용하여 프로세스를 백그라운드에 배치합니다.
node index.js &
프로세스 ID가 표시됩니다. 제 시스템에서는 15565
입니다.
[1] 15565
이제 SIGUSR1
시그널을 전송하여 CPU 프로파일링을 트리거합니다.
kill -SIGUSR1 <your_process_id>
부하 테스트를 다시 시작합니다.
npx autocannon --renderStatusCodes -d 11 -m POST -H "Content-Type: application/json" -b '{"password":"userPassword123"}' http://localhost:3000/register
부하 테스트가 완료되면 프로파일링을 중지하는 SIGUSR2
시그널을 보냅니다.
kill -SIGUSR2 <your_process_id>
프로파일링을 중지하면 수집된 프로파일링 데이터가 포함된 profile.cpuprofile
파일이 루트 디렉토리에 생성됩니다. 다행히도 이 파일은 Chrome에서 읽을 수 있습니다.
DevTools에서 빠르게 분석하려면 "Performance" 탭으로 돌아간 다음 Load profile 버튼을 클릭합니다.
Chrome은 파일을 분석한 후 이전 섹션에서 본 것과 같은 플레임 차트를 생성합니다.
다음 명령을 통해 노드 프로세스를 종료할 수 있습니다.
kill -9 <your_process_id>
perf
로 프로파일링Node.js 애플리케이션을 프로파일링하는 몇 가지 방법을 살펴보았으며 이제는 Node.js 애플리케이션과 다른 언어로 작성된 애플리케이션을 프로파일링할 수 있는 강력한 Linux 도구인 perf
를 사용하겠습니다. CPU 샘플 기록, 컨텍스트 스위치, 자세한 커널 정보 등 다양한 기능이 있습니다.
먼저, perf
가 설치되어 있는지 확인합니다.
perf --version
perf version 6.8.1
이제 --perf-basic-prof
플래그를 사용하여 Node.js 애플리케이션을 실행합니다.
node --perf-basic-prof index.js &
이렇게 하면 컴파일러가 코드를 기계어 코드로 변환할 때 파일 이름을 포함하도록 지시합니다. 이 옵션을 설정하지 않으면 perf
는 프로파일링 중에 함수 이름 대신 메모리 주소만 표시합니다.
그런 다음 프로세스 ID를 기록하고 perf
명령을 실행합니다.
sudo perf record -F 99 -p <your_process_id> -g
다른 터미널에서 부하 테스트를 진행할 수 있습니다.
npx autocannon --renderStatusCodes -d 11 -m POST -H "Content-Type: application/json" -b '{"password":"userPassword123"}' http://localhost:3000/register
부하 테스트가 완료되면 SIGINT(Ctrl-C)를 보내 perf
프로세스를 중지합니다. 출력은 다음과 같습니다.
...
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.240 MB perf.data (1156 samples) ]
perf
는 /tmp
폴더에 호출된 함수의 추적을 포함하는 /tmp/perf-<process_id>.map
과 같은 파일을 생성합니다. 결과를 집계하려면 다음을 실행합니다.
sudo perf script > perfs.out
이렇게 하면 바이너리 데이터가 포함된 perf.data
파일도 생성됩니다. 텍스트 편집기에서 perf.out
파일을 열어 콜 스택을 찾을 수 있습니다.
node 56676 444594.095023: 10101010 task-clock:ppp:
7db1716add39 cfree+0x19 (/usr/lib/x86_64-linux-gnu/libc.so.6)
2189d76 evp_md_ctx_clear_digest+0x36 (/home/stanley/.nvm/versions/node/v22.2.0/bin/node)
218a4e3 EVP_MD_CTX_copy_ex+0x73 (/home/stanley/.nvm/versions/node/v22.2.0/bin/node)
21c7d30 HMAC_CTX_copy+0x90 (/home/stanley/.nvm/versions/node/v22.2.0/bin/node)
22af0df kdf_pbkdf2_derive+0x3ef (/home/stanley/.nvm/versions/node/v22.2.0/bin/node)
21b2274 PKCS5_PBKDF2_HMAC+0x234 (/home/stanley/.nvm/versions/node/v22.2.0/bin/node)
....
374723 v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal
perfs.out
의 콜 스택 정보는 메모리 관리 (libc
의 cfree
)부터 시작하여 여러 암호화 함수(evp_md_ctx_clear_digest
, EVP_MD_CTX_copy_ex
, HMAC_CTX_copy
, kdf_pbkdf2_derive
PKCS5_PBKDF2_HMAC
)로 이동하면서 Node.js 프로세스의 실행 순서를 보여줍니다. 이 스택은 내부 Node.js 및 V8 엔진 작업을 계속하며, 이는 Node.js 런타임 환경 내에서 암호화 처리 및 함수 호출을 위해 CPU 리소스를 광범위하게 사용하고 있음을 나타냅니다.
이 파일을 읽는 것은 많은 양의 데이터로 인해 부담스러울 수 있으며 그다지 유익한 정보를 얻지 못할 수도 있습니다. 이를 이해하는 더 좋은 방법은 데이터를 시각화하는 것입니다.
이 데이터를 시각화하려면 FlameGraph 도구를 사용할 수 있습니다. 먼저 홈 디렉토리로 돌아갑니다.
cd ~
그런 다음 FlameGraph 리포지토리를 복제합니다.
git clone https://github.com/brendangregg/FlameGraph
디렉토리로 이동합니다.
cd FlameGraph/
그런 다음, perf.data
파일을 현재 디렉터리에 복사합니다.
cp ../nodejs-profiling-demo/perf.data .
이제 다음 명령을 사용하여 플레임 그래프를 만듭니다.
sudo perf script | ./stackcollapse-perf.pl |./flamegraph.pl > perf-flamegraph.svg
이 명령은 SVG 형식의 플레임 그래프인 perf-flamegraph.svg
파일을 만듭니다.
그런 다음 선택한 브라우저에서 perf-flamegraph.svg
파일을 열어 플레임 그래프를 확인합니다.
프레임을 읽으려면 먼저 색상은 주로 프레젠테이션 목적으로 사용되며 무작위일 수 있으므로 무시하세요.
각 상자는 함수에 해당하며, y축에는 콜 스택 깊이가 표시되고, 위쪽 상자는 현재 CPU를 사용하는 함수를 나타내고 아래쪽 상자는 부모 함수를 나타냅니다. x축은 함수의 CPU 시간을 보여줍니다. 각 박스의 너비는 느리거나 자주 호출되어 함수가 사용한 총 CPU 시간을 나타냅니다. 상자가 넓을수록 더 눈에 잘 띄므로 가장 넓은 상자를 먼저 살펴보세요. x축은 시간 경과를 나타내지 않고 유사한 함수를 함께 그룹화합니다.
플레임 그래프를 분석해보면 pbkdf2Sync
가 상당한 CPU 시간을 사용한다는 것을 알 수 있습니다. 이는 pbkdf2Sync
및 관련 암호화 함수를 나타내는 넓은 상자에서 분명하게 드러나며 CPU 사용량이 높음을 나타냅니다.
또 다른 흥미로운 기능은 상자를 클릭하여 자세한 내용을 보거나 검색을 수행할 수도 있다는 것입니다.
perf
명령을 사용하고 FlameGraph로 데이터를 시각화하면 애플리케이션의 CPU 사용량을 효과적으로 분석할 수 있습니다.
지금까지 성능 문제를 식별하기 위해 애플리케이션을 수동으로 프로파일링했습니다. 그러나 이는 특히 여러 서비스가 서로 다른 시스템에서 실행되는 마이크로서비스 아키텍처의 경우 까다로울 수 있습니다.
CPU 사용률을 지속적으로 최신 상태로 유지하려면 연속 프로파일링을 사용할 수 있습니다. 이는 애플리케이션을 지속적으로 프로파일링하는 동적 방법으로, CPU 사용량을 더 쉽게 모니터링하고 높은 메모리 또는 CPU 소비의 원인을 식별할 수 있습니다.
지속적인 프로파일링을 구현하려면 다음과 같은 도구를 사용할 수 있습니다.
이러한 도구를 사용하면 애플리케이션의 CPU 사용률을 효과적으로 모니터링하고 최적화할 수 있습니다.
이 글에서는 높은 CPU 사용량 소스를 식별하기 위해 Node.js 애플리케이션을 프로파일링하는 다양한 기술과 도구를 살펴보았습니다. 프로파일링은 성능 최적화에 중요하지만 애플리케이션을 더욱 향상시키려면 이 가이드에서 배울 수 있는 메모리 누수 감지 및 방지에 대해 학습해야 합니다.
🚀 한국어로 된 프런트엔드 아티클을 빠르게 받아보고 싶다면 Korean FE Article을 구독해주세요!
Great