Web/JavaScript

[Node.js] 런타임 / V8 /이벤트 기반 아키텍처/ K6

wjdwwidz 2024. 2. 22. 02:05

싱글스레드

자바스크립트 는 싱글스레드 언어이다

싱글스레드== 콜스택이 하나만 있다 == 한 번에 하나의 작업만 가능하다

 

 

V8엔진

자바스크립트와 *웹어셈블리 엔진 중 하나 , 자바스크립트를 바이트코드로 컴파일하고 실행하는 방식

V8엔진은 두 가지로 이루어져있다.

  • 메모리 힙 (Memory Heap) : 메모리 할당이 이루어지는 곳
  • 콜 스택 (call Stack) : 코드가 실행되면서 스택 프레임이 쌓이는곳

자바스크립트 엔진

 

런타임 환경 

런타임, 프로그램 언어가 구동되는 환경. 자바스크립트 엔진보다 더 큰 범위의 개념.

브라우저에서 자바스크립트가 실행된다면 브라우저가 런타임 환경일 것이고, node.js에서 실행된다면 런타임 환경은 Node.js 이다.

 

자바스크립트 런타임 환경

 

모든 브라우저는 자바스크립트 엔진 뿐 아니라 webAPI도 제공되는 자바스크립트 런타임을 내장하고 있다.

 

Web APIs

web APIs는 브라우저에서 제공하는 API 모음으로 비동기적으로 실행되는 작업을 처리한다. HTTP 요청을 보내거나, DDM이벤트를 듣거나 실행 연기 하는 등의 작업이 가능하다. (자바스크립트 언어에 내장된 것이 아니라 브라우저에 내장된 것이다.) web APIs를 이용하여 백그라운드에서 비동기적으로 작업을 처리하고, 해당 작업을 마치면 콜스택에 해당 작업이 끝났음을 알려줘서 그 작업을 수행할 수 있도록 한다. 

 

Web APIs의 종류

 

이벤트 기반 아키텍처

Node.js는 이러한 이벤트 아키텍처를 구성했기에 싱글스레드 환경에서도 거의 동시에 처리할 수 있다.

 

 

이벤트루프

https://www.youtube.com/watch?v=8aGhZQkoFbQ

 

 

 

이벤트루프가 하는 일 : 이벤트루프는 콜스택과 콜백 큐를 주시한다.

 

1. 콜스택이 비어있을 경우 콜백 큐에 있는 작업들을 하나씩 콜스택으로 이동시킨다.

2. 작업이 완료되면 (이벤트루프는) 결과를 콜백 함수 형태로 큐에 넣고

3. (이벤트루프는) 다시 이 값을 콜스택으로 전달하여 작업을 마무리한다.

 

 

자바스크립트의 setTimeout 이나 fetch와 같은 비동기 코드를 Web APIs에게 맡기고 백그라운드 작업이 끝난 결과를 콜백함수 형태로 큐에 넣고, 처리 준비가 되면 콜스택에 넣는다.

 

이벤트루프

  • WebAPIs : 브라우저에서 제공하는 API 모음으로 비동기적으로 실행되는 작업을 전담
  • Callback Queue : 비동기적으로 작업이 완료되면 실행되는 함수들이 대기하는 공간
  • Call Stack : 자바스크립트 엔진이 코드 실행을 위해 사용하는 메모리 구조

 

실험

hello.js

//모듈을 읽어오는 함수(require)
const http = require("http");
let count = 0;

//createServer : 서버 인스턴스를 만드는 함수 , 인수로는 콜백 함수를 받아 해당 요청을 처리
const server = http.createServer((req, res) => {
  //콜백 함수는 요청 처리에 사용할 요청 객체(req)와 응답 객체(res)를 인수로 받는다.
  log(count);
  res.statusCode = 200; //결괏값 200;
  res.setHeader("Content-Type", "text/plain"); //헤더 설정 : text/html 이라면 html을 text로 해석한다는 뜻
  res.write("hello\n"); //응답으로 hello\n 을 보내준다

  setTimeout(() => {
    res.end("Node.js"); //2초 후 Node.js 출력
  }, 2000);
});

function log(count) {
  console.log((count += 1));
}

server.listen(8000, () => console.log("Hello Node.js"));

 

 

test_hello.js

 

100명이 10초동안 계속 요청을 보내는 상황

import http from "k6/http";

export const options = {
  //테스트 옵션
  vus: 100, //vertual user 수 (가상 유저)
  duration: "10s",
};

export default function () {
  http.get("http://localhost:8000"); //테스트에 사용할 함수 지정
}

 

 

 

K6 이용 성능 테스트

scenarios : 가상 유저 100명으로 최대 40초동안 테스트 (실제 테스트 시간은 10초, *gracefulStop의 기본값 30초를 더해서 40초)

http_req_duration : 평균 2.01초 , p(90)=2.05초 이므로 90%의 요청이 2.05초 이하

http_req_failed : 실패 0번

http_reqs : http : 요청이 500번 발생

iterations_duration : http요청이 한번 완료되고 다시 시작될 때까지 걸리는 시간, 평균 2.01초

 

 

 

hello.js에서는 요청 하나당 2초 딜레이가 있다. 즉 요청 하나가 완료되는 데 2초가 걸린다. 스레드가 하나이므로 동기식 코드라면 200초가 걸려야 한다. 하지만 setTimeout은 이벤트 루프를 통해 비동기로 처리된다. 실제로 대략 2초 걸리는 요청 100개를 거의 동시에 처리했음을 알 수 있다. 

 


* gracefulStop : "가상 유저를 테스트 중에 변경하는 시나리오"에서 갑자기 유저를 변경하면, 데이터가 급변하는 현상이 생기게 되므로 최소 30초 동안은 기존 유저값이 유지된다는 의미. 위에서는 유저 수를 동적으로 조절하지 않으므로 큰 의미가 없으며 실제 테스트 시간인 10초 동안만 실행.

 

* 웹어셈블리 : C나 C++와 같은 프로그래밍 언어를 컴파일해서 어느 브라우저에서나 빠르게 실행되는 형식으로 바꿔주는 기술