공사중

쉽게 정리한 네트워크 | 비동기 이벤트 기반(async event-driven)이란? 본문

개발 | 네트워크

쉽게 정리한 네트워크 | 비동기 이벤트 기반(async event-driven)이란?

행운개발자 LuckyDeveloper 2020. 11. 1. 15:20

본 포스팅은 아래의 주제를 다룹니다.

  • Blocking I/O
  • Multi Thread + Blocking I/O의 한계
  • Multi Thread + Async I/O로의 극복
  • Single Thread + Async I/O
  • Event Loop

 

Blocking I/O

Blocking I/O는 하나의 프로세스가 어떤 자원을 사용하고자 할 때, 그 자원을 다른 프로세스가 점유하고 있다면 그 프로세스가 그 자원의 내용을 끝마칠 때까지 기다려야 한다는 것을 의미합니다. 애플리케이션에서 read()함수를 호출하면 운영체제의 커널에게 시스템 콜이 날라갑니다. 커널은 파일을 일기 위한 동작을 수행하고 파일을 모두 읽기 전에는 read()함수는 return되지 않습니다. 

 

Multi Thread + Blocking I/O의 한계

예전에 Blocking I/O를 사용할 때에는 대부분의 애플리케이션이 멀티 스레드를 사용할 수밖에 없었습니다. 멀티쓰레드는 개발자 입장에서 직관적으로 멀티 태스킹을 구현할 수 있는 수단이기도 했습니다. CPU scheduling도 결국 CPU라는 하나의 자원을 여러 스레드들이 CPU를 점유하기 때문에 기다릴 수밖에 없다는 근본적인 문제는 어쩔 수 없기 때문에 논외입니다. 

 

하지만 네트워크에서 동시에 대규모 요청을 동시에 처리하는 경우에는 꼭 개선해야하는 비효율성이 발견됩니다. 바로 각각의 스레드가 Blocking I/O를 수행할 때 I/O가 끝날 때까지 기다리는 문제입니다. 만약 4개의 Thread를 생성해서 사용하는 Thread Pool의 경우, 대용량 Network I/O가 필요한 요청 4 개를 받으면 전체가 I/O를 기다려서 새로운 요청에 응답하지 못하게 됩니다. 

 

Multi Thread + Async I/O로 극복

위와 같은 문제를 해결하기 위해서 아래와 같은 방식으로 동작하는 Aync I/O가 등장합니다.

 

  1. thread를 생성합니다.
  2. 비동기 I/O 명령을 통해 읽기를 요청합니다. ( 비동기 결과를 받기 위한 token을 받거나(APM), 완료 이벤트를 받습니다.(EAP)
  3. thread가 죽습니다.
  4. thread가 죽은 상태로 (thread context switching 비용이 감소한 상태로) 비동기 I/O가 수행됩니다.
  5. APM 또는 EAP로 읽기가 완료되었음을 통지 받습니다. 동시에 thread가 생성됩니다.
  6. 실제 data를 읽습니다.
  7. thread를 종료합니다.

context switching 비용이 감소한다는 것은 단위 시간당 처리량이 N배 높아졌다는 것이고, 이는 곧 동시접속자를 N배정도 더 견딜 수 있다는 의미가 됩니다. 

single thread + event-driven aync I/O

Node.js의 동작과정으로 설명을 해보겠습니다. Node.js는 Process는 I/O 요청이 들어오면 I/O 작업 처리에 대한 응답을 기다리지 않고(async), worker thread가 바로 다음 작업(다른 요청을 기다림)을 실행합니다. 대신 I/O 작업이 종료되면 이벤트를 발생시키고, 이 이벤트는 해당 프로세스의 event-queue에 등록됩니다. 프로세스는 event-queue에 등록된 새로운 이벤트를 감지하여, 해당 이벤트 시 수행해야 할 작업을 실행합니다. 여기서 어쩔 수 없이 다른 요청을 기다리는 스레드와 I/O 작업을 수행하는 스레드는 동시에 수행됩니다. 따라서 여기서 single thread라는 말은 "하나의 Master thread에서 사용자의 요청을 응답한다는 점을 의미합니다. 다른 Worker Thread에서는 이벤트 큐에서 I/O 작업만을 수행할 때에는 다음에  설명할 이벤트 루프의 개념이 사용됩니다. 

 

이벤트 루프(Event Loop)

이벤트 루프(Event Loop)는 작업을 요청하면서 그 작업이 완료되었을 때 어떤 작업을 진행할지에 대한 콜백 함수를 지정하여 동작이 완료되었을 때 해당 콜백 함수를 실행되는 방식의 동작 방식을 말합니다.

만약 클라이언트가 웹 서버에 HTTP 형식으로 요청하게 되면 서버에서는 이벤트 루프(Master Thread)가 계속 돌고 있다가 이를 감지하고 알맞은 작업을 워커 쓰레드를 생성하여 실행합니다. 이때 이벤트 루프는 해당 워커 쓰레드가 작업을 마친 뒤 그 결과와 함께 응답할 때까지 기다리는 것이 아니라 바로 루프로 복귀하여 다른 요청을 기다리게 됩니다.

다시 말해 이벤트 루프는 어떤 요청이 발생하면 그 작업에 대해 Worker 쓰레드 실행만을 일으킬 뿐입니다. 이후 작업을 할당받았던 해당 쓰레드가 모든 작업을 마치면 미리 전달받은 콜백 함수를 실행하도록 이벤트 루프로 응답하게 되며 이벤트 루프는 콜백함수를 실행하는 주체가 되어 클라이언트에게 결과를 응답해줍니다. 


참조