[Node.js] ์ค๋ ๋์ Event Loop
Node.js ํ๊ฒฝ์ ๋ํด์ ์ด์ผ๊ธฐํ๊ฑฐ๋ ํ๊ฐํ ๋ ์์ฃผ ๊ฑฐ๋ก ๋๋ ์ฃผ์ ์ค ํ๋๊ฐ ์ค๋ ๋์ ํ์ฉ์ฑ์ด๋ค.
ํํ Node ํ๊ฒฝ์ ๊ฐ๋ฆฌ์ผ์ ์ฑ๊ธ์ค๋ ๋ ํ๊ฒฝ์ ๋ฐํ์์ด๋ผ๊ณ ๋งํ๊ณค ํ๋ค.
๊ทผ๋ฐ ์ฌ์ค ๋ ๋ฉํฐ์ฝ์ด ํ๊ฒฝ์ ์์ ํ์ฉํ์ง ๋ชปํ๋ ๊ฒ๋ ์๋๋ค. ์ ํํ ๋งํ๋ฉด I/O ๋์์ ๋ํด์๋ง ๋ฉํฐ์ค๋ ๋ ํ์ฉ์ ๋ณด์ฅํ ์ ์๋ค.
์ฑ๊ธ ์ค๋ ๋?
Node.js๋ C/C++, Java, C# ๊ฐ์ ๊ธฐ์ฑ ์ธ์ด๋ค๊ณผ ๋ค๋ฅด๊ฒ ์ค๋ ๋๋ฅผ ์ง์ ์ ์ดํ์ง ๋ชปํ๋ค.
๋ค์ดํฐ๋ธ ์ค๋ ๋๋ ๊ฒฝ๋ ์ค๋ ๋๋ฅผ ํธ์ถํด์ ์ง์ ๋ฉํฐ์ฝ์ด๋ฅผ ์ฅ์ด์ง CPU ์ง์ฝ์ ์ธ ์ฐ์ฐ์ ํจ์จ์ ์ผ๋ก ํด๋ผ ์๋ ์๋ค๋ ๋ง์ด๋ค.
Promise ๊ฐ์ ๋น๋๊ธฐ ๋จ์๋ฅผ ํตํด ๋์์ฑ์ ๊ตฌํํ ์๋ ์์ง๋ง, ๋ฉํฐ์ค๋ ๋์ ๊ฐ์ ๋ณ๋ ฌ์ฑ์ ๋ด๋ณดํ์ง๋ ๋ชปํ๋ค.
๊ทธ๋ฅ tick ๊ธฐ๋ฐ์ผ๋ก ์๋ถํ ์ฒ๋ฆฌ๋ง์ ์ํํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
์ด๋ฐ ๋ถ๋ถ์์๋ ํ์ด์ฌ, ๋ฃจ๋น์ ๋น์ค๋ฌด๋ฆฌํ ์ฑ๊ธ์ค๋ ๋ ์ธ์ด๋ผ๊ณ ํ ์๋ ์๋ค.
Event Loop!
ํ์ง๋ง Node.js๋ ์ด๋ฒคํธ ๋ฃจํ๋ผ๋ ๋งค์ปค๋์ฆ์ ํตํด I/O ์์
์ ํจ์จํํ๋ค.
I/O ์ง์ฝ์ ์ธ ํ๊ฒฝ์์๋ ๋ฉํฐ์ค๋ ๋๋ฅผ ์ถฉ๋ถํ ํ์ฉํ๋ค๊ณ ํ ์ ์๋ค.
https://www.geeksforgeeks.org/node-js-event-loop/
Node.js๋ ๋ด๋ถ์ ์ผ๋ก libuv๋ผ๋ ๋ณ๋ ฌ์ฒ๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋๋ฐ, ์ด๊ฑธ ํตํด ์ค๋ ๋ํ์ ๊ตฌ์ฑํ๊ณ I/O ์์
์ ๋ค์คํํ๋ค.
๊ทธ๋ฆฌ๊ณ libuv๋ ๋ ๋ด๋ถ์ ์ผ๋ก epoll(Linux), kqueue(Mac), IOCP(Windows) ๊ฐ์ ์ปค๋ ์์ค์ ๋คํธ์ํน ๋งค์ปค๋์ฆ์ ์ฌ์ฉํด์ ์ปค๋ ์ค๋ ๋๋ฅผ ํ์ฉํ๋ค.
๋ฐฉ์์ ๊ฐ๋ตํ ์ ๋ฆฌํ๋ฉด ์ด๋ ๋ค.
- File I/O๋ Network I/O ๊ฐ์ ์์ ์ด ๋ฐ์ํ๋ฉด, ๋ฐํ์์ ๊ทธ ์์ฒญ์ ์ด๋ฒคํธ ํ์ ๋ฃ๋๋ค.
- ์ด๋ฒคํธ ๋ฃจํ๋ ์ด๋ฒคํธ ํ์ ์์ ์ด ๋ค์ด์ค๋ฉด ์ค๋ ๋ํ์ ํ์ฉํด ์์ ์ ์ฒ๋ฆฌํ๋ค.
- ์ค๋ ๋์์์ ์์ ์ด ์๋ฃ๋๋ฉด ์ด๋ฒคํธ๋ฃจํ๋ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ฒคํธ ํ๋ฅผ ํตํด Javascript ์ธก์ ์๋ตํ๋ค.
์ด๋ฐ ๊ตฌ์กฐ ๋๋ถ์ ์๋ฒ ํ๊ฒฝ์์๋ ๊ทธ๋ญ์ ๋ญ ๋์์ง ์์ ํผํฌ๋จผ์ค๋ฅผ ๋ณด์ฌ์ค ์ ์๋ ๊ฒ์ด๋ค.
์ฐ์ฐ ์ฑ๋ฅ์ด ๊ตฌ๋ฆฌ๊ธด ํ๋ฐ, ์ด์ฐจํผ ์๋ฒ ๊ตฌ์กฐ๋ ๋คํธ์ํฌ I/O๊ฐ ๋๋ถ๋ถ์ด๊ณ , ๋๋ถ๋ถ์ ๋ฌด๊ฑฐ์ด ์ผ์ DB๊ฐ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
์ข ํน์ํ ์์ญ์ผ๋ก ๊ฐ๋ฉด ๋ง์ด ๋ฌ๋ผ์ง ์๋ ์์ง๋ง, ๋๋ถ๋ถ์ ์คํํธ์
์์ค์์ ์๊ตฌํ๋ ๊ธฐ๋ฅ ๋ช
์ธ์์๋ ์ฑ๋ฅ์ด ๊ทธ๋ค์ง ๋ถ์กฑํ์ง ์๋ค.
์กฐ๊ธ ๋ ๋ค์ด๊ฐ๋ณด๋ฉด, ์ด๋ฒคํธ๋ฃจํ์ ๊ฐ ๋ฃจํ ์คํ ์ ์ด๋ฐ ์์ผ๋ก ๊ตฌ์ฑ๋๋ค.
https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick
-
timer: ์ผ๋จ ํ์ด๋จธ ๊ด๋ จ ์์ ์ ํ์ธํด์ ๋จผ์ ์ฒ๋ฆฌํ๋ค. setTimeout์ด๋ SetInterval๋ก ์์ฝํ ํจ์๊ฐ ์ด์ ํด๋น๋๋ค.
-
pending callbacks: ์ด์ ๋ฃจํ์์ ๋ณด๋ฅ๋ ์ฝ๋ฐฑ์ ์คํํ๋ค.
-
idle, prepare: ์ด๊ฑด ๋ชฐ๋ผ๋ ๋๋ค. ๋ด๋ถ์ ์ผ๋ก GC๋ฅผ ์ํ ํ์ธ์ด๋ ์ก๋คํ ๋ด๋ถ๋์์ ์ํด ์กด์ฌํ๋ค.
-
poll: ์๋ก์ด I/O ์ด๋ฒคํธ๋ฅผ ๋ฐ๊ณ ์ฒ๋ฆฌํ๋ค. readFile ๊ฐ์ ํจ์๊ฐ ์ฌ์ฉ๋๋ฉด ๊ทธ๊ฑธ ๋ฐ์์ฃผ๋ ๊ฒ์ด๋ค. ์ฌ์ค ์ด๊ฒ ์ ์ ์จ์ 99% ์ ๋๋ ๋๋ค.
-
check: ์ด๋ฒคํธ ํ์ ์ถ๊ฐ๋ ๋ชจ๋ setImmediate() ์ฝ๋ฐฑ์ ์ฒ๋ฆฌํ๋ค.
-
close callbacks: "์์ผ ์ฐ๊ฒฐ ๋ซ๊ธฐ" ๊ฐ์ ์ ๋ฆฌ ์์ ์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ์ํํ๋ค.
๋ฌดํ๋ฃจํ ๋บ๊ธ๋บ๊ธ ๋๋ฉด์ ์ ์คํ
๋ค์ ๊ณ์ ๋ฐ๋ณตํ๋ ๊ฒ์ด๋ค.
๊ทธ๋ฆฌ๊ณ Event Loop ์์ฒด๋ ๋ฉ์ธ ์ค๋ ๋ ์์์ ๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ๋ก ์
์ง์ ์ธ ๋ฌธ์ ๋ฅผ ๋ธ๋ค๋ฉด ๋จ๊ณจ๋ฉ๋ด๋ก ๋์ค๋ ์์ฌ๋ค.
์๋ฆฌ๋ฅผ ์ ์๋ฉด ๋๋ฝ๊ณ ๋น์ง๊ด์ ์ธ ์ฝ๋๋ฅผ ์ง๊ธฐ ์ข๋ค.
์ด๋ฒคํธ ๋ฃจํ์ ์ฝ๋๋ ์ฌ๊ธฐ์ ๋ณผ ์ ์๋ค.
https://github.com/nodejs/node/blob/main/deps/uv/src/unix/core.c#L378-#L417
์ต์ ํ ํฌ์ธํธ: setImmediate
setImmediate๋ ํน์ํ ์ต์ ํ ํฌ์ธํธ๊ฐ ํ์ํ ๋ ์ฌ์ฉ๋๋ ํน์ํ ํจ์ ์ค ํ๋๋ค.
์์ ์ธ๊ธํ๋ฏ์ด setImmediate๋ ์ด๋ฒคํธ ๋ฃจํ ์คํ
์ค poll ๋ค์์ผ๋ก ์ฒ๋ฆฌ๋๋ค.
๊ฑฐ์ ๋ง์ง๋ง ๋ถ๋ถ์ ์คํ๋๋ ์
์ธ๋ฐ, ์ด๊ฑธ ์ด์ฉํด์ ํ์ฌ ์์ ์ด ์ฒ๋ฆฌํ๋ ์์
์ ์ฐ์ ์์๋ฅผ ๋ค๋ก ๋ฏธ๋ฃฐ ์ ์๋ค๋ ๊ฒ์ด๋ค. ๋ฆฌ์์ค๋ฅผ ์๋ณดํ๋ ๊ฒ์ด๋ค.
๊ทธ๋์ ๋ญ๊ฐ ์ค๋ซ๋์ CPU๋ฅผ ์ก์๋จน๋ ๋ฌด๊ฑฐ์ด ์ฐ์ฐ์ด ์๋ ๊ฒฝ์ฐ์๋ setImmediate๋ก ๋์์ ๋ค๋ฅธ ๋ ์ค์ํ I/O ์์ ์ด ์ฐ์ ์ผ๋ก ์ฒ๋ฆฌ๋๊ฒ๋ ์ ๋ํ๊ณค ํ๋ค.
๊ด๋ จ ํฌ์คํธ
https://rclayton.silvrback.com/scheduling-execution-in-node-js
์ต์ ํ ํฌ์ธํธ: Event Loop Lag
Event Loop์ ์์์ ์ธ๊ธํ๋ฏ ์ฃผ๊ธฐ์ ์ผ๋ก ๋ฉ์ธ ์ค๋ ๋๋ฅผ ์ ์ ํ์ฌ ๋๊ธฐ ์ค์ธ ์์
๋ค์ ์ฒ๋ฆฌํ๋ค.
์ด๋, ๋ฉ์ธ ์ค๋ ๋๊ฐ ๋ค๋ฅธ ์์
์ผ๋ก ์ธํด ์ ์ ๋์ด Event Loop๊ฐ ๋๊ธฐํ๋ ์๊ฐ์ด ๊ธธ์ด์ง๋ ๊ฒ์ Event Loop Lag์ด๋ผ๊ณ ํ๋ค.
I/O๊ฐ ์ด๋ฒคํธ๋ฃจํ๋ฅผ ํตํด ๋น๋๊ธฐ๋ก ๋๋ค๊ณ ๋ ํ์ง๋ง, ์ด๋ฒคํธ๋ฃจํ ์์ฒด๊ฐ ๋๊ธฐ ์ฝ๋์ ์ํด์ ๋ธ๋ญ๋ ์ ์๋ ๊ตฌ์กฐ๋ก ๋์ด์๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ๋ ๋ฌธ์ ๋ค.
์๋ฌดํผ Lag์ด ๋๋ค๋ฉด ํน์ ํ ์์ ์ด ๋ฉ์ธ ์ค๋ ๋๋ฅผ ์ค๋ ์ก๊ณ ์๋ค๋ ๊ฒ์ผ๋ก, Node.js์ Event Loop ๊ตฌ์กฐ๋ฅผ ์ด์์ ์ผ๋ก ํ์ฉํ๊ณ ์์ง ๋ชปํ๋ค๋ ๋ป์ด ๋๋ค.
1. Lag ์ธก์ ๋ฒ
Lag ์ธก์ ์ฉ์ผ๋ก ์ ๊ณต๋๋ Node.js ์ ์ฉ ๋๊ตฌ๋ค์ด ์กด์ฌํ๋ค.
๋๋ด๊ณ ์ธ๊ฑฐ๋ผ๋ฉด New Relic, Appdynamics, Dynatrace ๊ฐ์ ์ต์
์ด ์๊ณ , ์คํ์์ค ๋ฒ์ ์ผ๋ก๋ ์ด๋ฐ๊ฒ ์๋ค.
https://www.npmjs.com/package/nodejs-dashboard
2. Lag ๊ฐ์
๊ณผ๋ํ Lag์ ๊ฐ์ ํ๋ ๊ฒ์๋ ๋ช๊ฐ์ง ์ต์ ์ด ์กด์ฌํ๋ค.
-
๋๊ธฐ ์ฝ๋๋ฅผ ์ต๋ํ ๋ค ๋น๋๊ธฐ ์ฝ๋๋ก ์ ํํ๋ค.
-
worker_threads API ์ฌ์ฉ
-
C++๋ก ๋ค์ดํฐ๋ธ ์ ๋์จ ์ง์ ๋ฐ๊ธฐ
-
child_process ์ฌ์ฉ
ํ์์ ์ํฉ์ ๋ฐ๋ผ์ ์ ์คํ๊ฒ ์ ํํ๋ฉด ๋๋ค.
์ฐธ์กฐ
https://medium.com/nodejsmadeeasy/nodejs-event-loop-lag-5d5928fd03c
์ต์ ํ ํฌ์ธํธ: Event Loop Utilization
Event Loop Utilization์ Event Loop๊ฐ ์ผ๋ง๋ ๋ฐ์๊ฒ ์์ ์ ํ๊ณ ์๋์ง ๋ํ๋ด๋ ์ต์ ํ์ฉ ์์น๋ค.
์ผ๋ฐ์ ์ผ๋ก 0๊ณผ 1 ์ฌ์ด์ ๊ฐ์ ๊ฐ๋๋ฐ, 1์ ๊ฐ๊น์ธ ์๋ก Event Loop๊ฐ ์ฌ๋ ์๊ฐ ์์ด ํญ์ ์ด๋ค ์์
์ ์ฒ๋ฆฌํ๊ณ ์๋ค๋ ๊ฒ์ด๋ค.
๋ง์ฝ ์๋ฒ๊ฐ ์ด๋ฏธ Max RPS์ ๋๋ฌํด์ ๋ ์ด์์ ํธ๋ํฝ์ ๋ฐ์ง ๋ชปํ๊ณ ์๋๋ฐ, Event Loop Utilization์ด 1๋ณด๋ค ๋ฎ๋ค๋ฉด Loop๋ฅผ ์ถฉ๋ถํ ํ์ฉํ์ง ๋ชปํ๊ณ ์๋ค๋ ๊ฒ์ด๋ค.
Event Loop Utilization๋ Node.js์์ ์ ๊ณตํ๋ perf_hooks๋ผ๋ API๋ฅผ ํ์ฉํ๋ฉด ์ธก์ ํ ์ ์๋ค.
https://nodejs.org/api/perf_hooks.html
์ฐธ์กฐ
https://blog.platformatic.dev/the-nodejs-event-loop
์ฐธ์กฐ
https://nodesource.com/blog/event-loop-utilization-nodejs/
https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick
https://github.com/libuv/libuv
https://stackoverflow.com/questions/63770952/nodejs-setimmediate-function-realtime-usecase-and-example
https://www.korecmblog.com/blog/node-js-event-loop