개요

SSAFY 계절학기 강의 수강 중 싱글스레드에서 비동기 요청을 할 수 있다는 내용이 있었는데, 필자가 개발하면서 싱글 스레드 환경에서 비동기 요청을 한 기억이 없어서 그런지 잘 이해가 되지 않았다. 이를 이해하고자 문서를 작성해본다.

자바스크립트에서 싱글 스레드와 비동기 요청

아래와 같이 “싱글스레드 비동기” 라는 키워드로 구글에 검색을 해봤다. 그러나 이 부분은 자바스크립트 언어가 어떻게 싱글 스레드에서 비동기 요청 처리를 가능하게 하는 것인지 말해주는 내용이 대다수 였다. 글들을 읽어보면 결국에는 자바스크립트 언어 자체는 싱글 스레드로 동작하는 것이고, 비동기 요청의 경우 Browser API를 통해 비동기 요청을 처리 후 콜백 처리를 해주는 것으로 이해했다.

따라서, 자바스크립트도 결국에는 완전한 싱글 스레드가 아니라고 생각했다.

그렇다면 완전한 싱글스레드 환경에서 비동기 요청은 가능한 것일까?

알고리즘을 풀 때 처럼 main 함수만 있는 환경에서도 비동기 요청이 가능할 지 궁금했다.

대표적인 비동기 요청 사례인 File I/O를 통해 이를 알아보자.

동기 및 비동기 I/O

동기 파일 I/O에서 스레드는 I/O 작업을 시작하고 I/O 요청이 완료될 때까지 즉시 대기 상태로 돌입합니다. 비동기 파일 I/O를 수행하는 스레드는 적절한 함수를 호출하여 커널에 I/O 요청을 보냅니다. 커널에서 요청을 수락하는 경우, 커널이 스레드에 I/O 작업이 완료되었다는 신호를 보낼 때까지 호출 스레드가 다른 작업을 계속 처리합니다. 그런 다음 현재 작업을 중단하고 필요에 따라 I/O 작업에서 데이터를 처리합니다.

 

아래는 동기 I/O와 비동기 I/O를 그림으로 표현한 것이다.

 

간단하게 정리해보면, I/O 처리의 경우 커널모드 스레드에서 수행된다. 싱글 스레드라고 믿었던 프로그램도 사실은 커널모드 스레드를 통해 I/O 작업을 수행하고 있다. 따라서 위와 같은 파일 I/O 작업의 경우 싱글스레드 프로그램에서도 비동기 요청이 가능하다. 다만, 파일 I/O의 경우 커널모드의 스레드를 이용하므로, 이와 같이 타 스레드에 작업을 위임하지 않는 이상 싱글 스레드에서 비동기 요청은 불가능하다고 생각한다.

싱글 스레드에서 비동기, 효율적인가?

서비스를 개발하면서 싱글 스레드에서 비동기 요청을 하는 경우를 많이 이용해본 적이 없다. 왜일까? 만약, 파일 읽기 요청과 다른 작업을 함께 수행한다고 생각해보면 굉장히 불편할 것이라고 생각한다. 다른 작업을 수행하면서 수시로 커널에 I/O가 완료 되었는지 물어봐야 할 것이고, 에러처리도 어려울 것이다.

비동기 처리 아키텍쳐

Spring, Android와 같은 많은 프레임워크에 비동기 처리를 지원하는 API를 포함하거나, 라이브러리 형태로 사용하기 좋게끔 만들어져 있다. 이러한 라이브러리들은 어떤 구조로 만들어져 있을까? 대표적인 비동기 요청 라이브러리인 Spring WebClient를 통해 알아보자.

Spring WebFlux includes a client to perform HTTP requests with. WebClient has a functional, 
fluent API based on Reactor, see Reactive Libraries, which enables declarative composition 
of asynchronous logic without the need to deal with threads or concurrency. It is fully 
non-blocking, it supports streaming, and relies on the same codecs that are also used to 
encode and decode request and response content on the server side.

Spring WebFlux에 내장된 WebClient의 경우 netty를 기반으로 만들어졌다고 한다. netty는 Reactor Pattern으로 만들어졌다고 하는데, Reactor Pattern에 대해서도 간단히 알아보자.

Reactor Pattern

하나 이상의 클라이언트로부터의 요청을 동시 처리하기 위해서 사용하는 패턴이다.

어플리케이션에서 이벤트를 처리하기 위한 루프를 도는 것이 아니라, 이벤트에 반응하는 객체(reactor)를 만들고, 이벤트가 발생하면 어플리케이션 대신 reactor가 반응하여 처리하는 것이다.

정리

이와 같이 싱글 스레드에서 비동기 요청을 할 수는 있지만, 사용되지 않는 이유가 있는 것 같다. 결론을 내보자면 아래와 같다.

  • 싱글 스레드에서 일부 작업의 경우 비동기 요청이 가능하다.
  • Reactor Pattern이 등장 한 것처럼 비동기 요청의 경우 멀티 스레드를 이용하여 구현하는 것이 효율적일 것이다.

Reference

동기 및 비동기 I/O - Win32 apps

WebClient :: Spring Framework

리액터패턴 / 프로액터패턴

 

+ Recent posts