[스터디] Thread & Process with JS, async-await

suhwani·2024년 11월 3일
0
post-thumbnail

기본 개념


Process
프로그램을 메모리 상에서 실행 중인 작업
프로세스는 각각 별도의 주소 공간 할당 → 독립적

Thread
프로세스 안에서 실행되는 여러 흐름 단위

스레드끼리 공간, 자원을 공유

Process >>> Thread
프로세스마다 최소 1개의 스레드를 소유 (메인 스레드 포함)

Multi Process
하나의 프로그램을 여러 개의 프로세스로 구성하여 각 프로세스가 병렬적으로 작업을 수행하는 것

장점: 안정성 → 메모리 침범 문제를 OS 차원에서 해결
단점: 각각 독립된 메모리 영역을 갖고 있어, 작업량이 많을수록 오버헤드 발생 → Context Switching

Context Switching
프로세스 상태 정보를 저장하고, 복원하는 일련의 과정
동작 중인 프로세스가 대기하면서, 해당 프로세스의 상태를 보관하고, 대기하고 있던 다음 순번의 프로세스가 동작하면서 이전에 보관했던 프로세스 상태를 복구
프로세스가 각 독립된 메모리 영역을 할당받아 사용되므로, 무거운 작업이 진행되었을 때 오버헤드가 발생

Multi Thread
하나의 응용 프로그램에서 여러 스레드를 구성해 각 스레드가 하나의 작업을 처리하는 것

장점: 독립적인 프로세스에 비해 공유 메모리만큼의 시간, 자원 손실이 감소, 전역 변수와 정적 변수에 대한 자료 공유 가능
단점: 안전성, 하나의 스레드가 데이터 공간을 망가뜨리면 모든 스레드가 작동 불능 (공유 메모리 갖기 때문)
안전성 문제를 Critical Section 기법으로 해결

Critical Section(임계 영역) 접근 동기화
임계 영역은 여러 스레드가 동시에 접근하면 데이터 불일치나 충돌이 발생할 수 있는 공유 자원의 코드 영역을 의미.
예를 들어, 하나의 스레드가 데이터를 쓰고 있는 동안 다른 스레드가 동일한 데이터를 읽거나 수정하면, 데이터의 일관성이 깨져 전체 시스템이 불안정해질 수 있다.
해결 방법 - Lock, Mutex, Semaphore…

비동기란 무엇이고, 왜 필요한가…with Thread, Process


1. JavaScript 동작


위 사진은 JS 언어의 서버 프레임워크인 Express 코드 중 일부분이다.
만약 위 경로에 GET 요청이 온다면 어떤 순서로 출력이 될까?

정답은 “A-1” → “B-1” → “B-2” → “A-2” 이다.
그 이유는 코드가 동기적으로 실행되기 때문에 함수가 끝나길 기다렸다가 다음 출력문을 실행한다.

위 코드는 이전과 달리 async-await을 사용해서 함수를 실행하였다. 어떻게 출력될까?

정답은 “A-1” → “B-1” → “B-2” → “A-2” 이다. 기존과 동일하다.

2. Async-await을 사용하는 이유


async-await을 썼는데, 왜 기존과 동일한걸까? 그렇다면 굳이 쓸 필요가 있을까?
async 라는 말은 “비동기”라는 의미인데, 비동기라고 지정해놓고 왜 기다리는걸까?

위 사진은 다른 서버에 요청을 보내는 경우이다. 응답을 받고, 응답값을 사용한다.
response 변수는 아직 응답이 도착하지 않았지만, 일단 보내기만 하고 아래 코드를 순서대로 실행한다.

하지만 response 변수에 정확한 값이 담기지 않았기에
console.log(response.data) → “undefined”를 출력한다.
console.log(response) → Promise 을 출력한다.
이는 axios에서 Promise 지원하고 pending은 응답 대기 중인 상태를 의미한다.

그렇다면 await을 사용한다면 뭐가 달라질까?

console.log(response.data) → “Hello, World!”를 출력한다. 이는 await을 사용해서 response 값이 담기길 기다린 후에 다음 코드를 실행했기 때문이다.
따라서 async-await 을 이용해서 동기적인 흐름으로 코드를 작성하지만,
비동기적으로 await 지정한 함수가 끝마치기 전까지 해당 코드의 동작을 중지합니다
.

3. 비동기 처리와 Thread 관계


JavaScript는 싱글 쓰레드 언어이다. async-await을 사용하면 동기적인 흐름이 중지한다.
동기적인 흐름이 중지되면, 쓰레드에 들어온 새로운 요청을 실행한다.

위 사진은 약 10초가 걸리는 API로 요청을 보내는 컨트롤러 코드이다.
0초부터 5초까지, 1초마다 위 API에게 요청을 보낸다면 어떻게 출력이 찍힐까?

정답은 [ A-1 → A-1 → A-1 → A-1 → A-1 → A-2 → A-2 → A-2 → A-2 → A-2 ] 이다.
await을 위해 대기하는 동안 다른 요청을 수행하는 것이다.

위 코드는 어떨까. 외부 API가 10초 걸리는 게 아닌 내부 함수가 10초가 걸리는 상황이다.
0초부터 5초까지, 1초마다 위 API에게 요청을 보낸다면 어떻게 출력이 찍힐까?

정답은 [ A-1 → A-2 → A-1 → A-2 → A-1 → A-2 → A-1 → A-2 → A-1 → A-2 ] 이다.
서버 내 함수가 실행되기 때문에, 계속 코드가 실행된다.

위 코드에서 API 요청을 보내고, “B-2”가 ****찍히기 전 새로운 API 요청을 보낸다면 이후 출력은 어떻게 될까?
A-1 → B-1 → {새로운 API 요청} → ???

정답은 [ A-1 → B-1 → B-2 → C-1 → C-2 → A-2 → A-1 → B-1 → B-2 → C-1 → C-2 → A-2 ] 이다.
Node.js는 여러 요청을 백그라운드 큐에 담아서 관리하고, 싱글쓰레드로 동작을 하는데, 이 때 동작의 기준은 한 개의 API 요청이다. 즉 1번 API 요청이 끝나지 않았기 때문에 쓰레드를 놔주지 않고, 계속 실행하는 것이다.

추가로 Node.js는 이벤트 루프라는 것이 있는데, 이벤트 루프가 오래 걸리는 작업(ex. DB 쿼리, File I/O)은 백그라운드에서 진행하도록 한다. 덕분에 JS는 싱글쓰레드 언어이지만, 지금까지 서버 프레임워크로 살아남을 수 있었다. 하지만 위에서 봤던 cpu 연산이 오래 걸리는 작업의 경우는 백그라운드로 처리하지 않는다.

4. 근데 트래픽이 엄청 몰리면 어떡하지


이벤트 루프를 이용해 백그라운드로 돌리고, async-await으로 비동기 처리를 한다고 해도 트래픽이 몰리면 싱글쓰레드라는 단점을 어떻게 보완할까

→ 프로세스 늘리기
1. Node.js는 멀티쓰레딩을 지원하기도 하고, Cluster모듈을 사용하면 프로세스를 추가로 생성 가능하다.
2. Docker를 이용해서 컨테이너를 만들고, Docker-compose로 replica를 설정한다. 또한 Nginx로 로드밸런싱 설정하여 트래픽을 여러 컨테이너에 분산할 수 있다.
3. PM2 프로세스 매니저를 사용한다. 다만 Docker 개발 이후 잘 사용되지 않는다.

5. 프론트는 트래픽이 몰리거나, 부하에 대해서 왜 얘기가 없을까


보통 트래픽 분산, 부하, 터지다 라는 표현은 서버측에서 많이 고민하는 주제이다.
근데 프론트는 왜 이런 얘기가 없는걸까?
10명 쓰던 서비스에서 갑자기 1만명이 들어오면 당연히 프론트도 터져야되는 거 아닌가?

  1. 트래픽 자체가 서버와 비교하면 훨씬 적은 수준이다.
    1-1. 클라이언트 앱을 가져오는 순간은 웹 사이트 접근 및 새로고침 누를 때 정도
    1-2. 반면 서버는 페이지를 구성하는 API 여러 개, 버튼 누르면 보내는 API 등등 훨씬 많다.
  2. 그럼에도 불구하고, 유저가 많다면 프론트엔드 서버에도 트래픽은 존재하기 때문에 뻗을 수 있다.
    2-1. 서버가 아니라 AWS S3를 사용한다. 초당 3500 ~ 5500개 요청을 감당 가능하다.
    2-2. 더 많은 트래픽을 감당해야 하는 경우 CDN, 로드밸런싱 등을 사용한다.
    2-3. EC2를 사용한다면 ELB, 스케일업을 고려한다.

6. 자바는요…?


요청을 2개 보내니까, 각 Thread에서 각 요청을 하나씩 수행한다. 하지만 static 변수인 “qq”를 제대로
더하지 못하는 모습이다. 이는 static 변수라서 모든 스레드가 접근할 수 있는데, Lock 없이 더해지고 있어 동시성 문제가 발생한다.
synchronized 사용하여 더한다면 해결된다.

profile
Backend-Developer

0개의 댓글