백엔드

블로킹 I/O vs 논블로킹 I/O 비교 (w/ RestTemplate vs WebClient)

연유뿌린빙수 2025. 4. 3. 03:20

 

블로킹

 

블로킹 방식은 어떤 요청이 들어온다면,
그 요청을 처리하는 스레드가 응답이 반환될 때까지 차단되어 대기하는 방식이다.

 

 

 

예를 들어 외부의 API에 특정 요청을 보낸다고 해보자.

 

그러면 이 처리 중의 과정에서 메인 스레드가 응답이 반환될 때까지 차단을하고 대기를 한다.

응답이 도착하면 그제서야 메인 스레드는 응답을 처리하고 결과를 반환한다.

 

이러한 구조는 구현이 단순하다는 장점이 있었고, 러닝커브가 낮아서 대부분 이러한 기반으로 기능을 구현하였으나,

I/O 대기 시간동안 CPU 리소스를 활용하지 못하는 비효율성이 존재햇다.

 

이를 보완하기 위해 멀티스레딩(Multi-threading) 기법을 통해 추가 스레드를 할당하는 형식으로 I/O 처리에 대한 효율성을 높여왔다.

즉, 요청마다 별도의 스레드를 할당하여 동시에 I/O 작업을 병렬로 처리함으로써 시스템의 처리량(Throughput)을 향상시키는 방식이다






멀티스레딩

그러나, CPU 대비 많은 수의 스레드를 사용하는 애플리케이션은 비효율적이다.
성능 저하의 요인을 겪게 된다.

어떠한 점들이 발생하는지 정리해보자.

 

 

1. 컨텍스트 스위칭 비용(Context Switching Overhead)

 

스레드가 많을 수록 컨텍스트 스위치이으로 인한 스레드 전환 비용이 발생한다.
이 과정에서 레지스터 상태, 프로그램 카운터, 스택 포인터 등의 정보를 저장하고 다시 로드해야하므로, 그만큼 CPU 자원이 낭비되고 지연이 발생한다.

 

 

2. 메모리 사용에 있어서 오버헤드가 발생(Memory Overhead)

 

각 스레드는 고유한 스택(Stack) 메모리를 보유하므로, 스레드 수가 증가할수록 메모리 사용량도 선형적으로 증가한다.
이는 전체 시스템 메모리의 효율성을 저하시킨다

 

 

3. 스레드 풀에서의 응답 지연 문제

 

스레드 풀에 여유 스레드가 없을 경우, 새로운 요청은 대기열(Queue) 에서 대기해야 한다.
반납된 스레드가 다시 사용 가능한 상태로 전환되기까지의 시간 동안 응답 지연(latency) 이 불가피하게 발생한다.

 

 

다음 그림을 확인해보자

 

프로세스와 스레드의 컨텍스트 스위칭

위의 그림은 CPU가 여러 프로세스를 교대로 실행하는 방식, 즉 프로세스 스케줄링(Process Scheduling) 의 개념을 보여준다.

 

여러개의 프로세스를 돌리는 것은 사용자인 우리의 눈에서는 인지하지 못하지만
CPU가 여러개를 빠른 속도로 전환하면서 실행하기 때문에 동시에 처리되는 것처럼 보인다.

 

 

하지만 각 프로세스가 교체될 때마다 CPU는 프로세스 제어 블록(PCB)에 현재 상태를 저장하고, 다음 프로세스의 상태를 로드해야한다.

그림을 자세히 봐보자.

 

위의 P1이 실행 후에 P2가 실행되는 것을 볼 수 있다. 그러나 문제는 여기서 나온다.
P1의 실행시간과 P2의 유휴 시간이 서로 정확히 일치하지 않는 것을 볼 수 있다.

 

PCB에 저장을 하고, PCB에서 로드하는데에 추가 시간이 들어가기 때문에 실행 시점과 유휴시간이 딱 들어맞지 않는 것이다.
PCB에 프로세스 상태를 저장하고 PCB에서 로드하는 시간 동안에 CPU는 대기하느라 일을 하지 못한다.

 

이 과정에서 추가적인 시간이 소요되며, 그 시간 동안 CPU는 실질적인 연산을 수행하지 못하고 유휴 상태(Idle) 가 된다.

스레드의 컨텍스트 스위칭 또한 동일한 원리로 동작한다.

 

물론 스레드는 동일한 프로세스 내의 메모리 공간을 공유하기 때문에 프로세스 전환보다 비용이 낮다.

그럼에도 불구하고, 스레드 수가 과도할 경우 스위칭 비용이 누적되어 CPU 효율이 저하된다.
스레드가 많아질수록 스택 메모리 및 스레드 관리 구조체에 대한 메모리 오버헤드가 발생한다.

 

이로 인해 스레드 풀의 효율이 저하되며, 스레드 풀 내에 유휴 스레드(Idle Thread) 가 부족할 경우 요청 처리 대기 시간이 늘어나게 된다.
스레드가 반환되어 다시 재사용 가능한 상태로 전환되는 과정에서도 추가적인 지연이 발생한다.



그래서 등장한 것이 논블로킹 방식이다!!!

논블로킹

논블로킹에서는 스레드의 차단이 없기 때문에 여러 개의 요청이 가능하다.

작업 스레드의 종료와 상관 없이 요청을 한 스레드는 차단 당하지 않는다.


논블로킹 방식은 요청을 보낸 스레드가 응답을 기다리며 차단되지 않고, 콜백(Callback), 이벤트 루프(Event Loop) 등을 활용하여 비동기적으로 결과를 처리한다.

 

적은 수의 스레드를 사용하기 때문에 스레드 전환 비용이 적게 발생한다.
따라서 CPU 대기 시간 및 사용량에 있어도 효율적인 방식이다.

결과적으로, 논블로킹 아키텍처는 고동시성(High Concurrency) 이 요구되는 서버 애플리케이션이나 I/O 중심형( I/O-bound) 시스템에서 특히 효과적이다.

 

그러나, CPU를 많이 사용하는 작업이 포함되어있을 경우에는 성능 향상에 악영향을 줄 수 있다.
또한 사용자 요청에서 응답까지의 과정에 블로킹 요소가 포함되어있을 경우,
논블로킹의 이점을 발휘하기가 어려운 편이다.



 

 

이에 대하여 HTTP Client를 통하여 비교를 하는 것도 가능하다.

 

RestTemplate와 WebClient를 통한 비교

RestTemplate: 전통적인 동기식 클라이언트

RestTemplate는 Apache HttpClient 라이브러리를 기반으로 만들어진, 아주 간단한 API 템플릿이다.

 

		ResponseEntity<Book> response = restTemplate.getForEntity(uri, Response.class);
		Book book = response.getBody();

 

다음과 같이, 요청을 보낼 uri와 어떤 HTTP 요청 방식으로 보낼지를 설정해주고, 반환 타입에 대해서도 지정해주면 된다.
이는 대표적인 간단한 동기식으로,

 

외부에 요청을 보낼 때 이에 대하여 응답이 올때까지 스레드를 기다리면 된다.(스레드가 응답 올때까지 멈춰있음)

 

또한 RestTemplate는 예외를 발생시켜 오류를 처리하는 등 전형적인 방식이고,
직렬화 및 역직렬화를 위하여 외부 라이브러리(Jackson과 같은)가 필요하다.



 

WebClient : 새로운 비동기식 클라이언트

WebClient는 Spring 5부터 등장한 최신 클라이언트로, 가장 주된 특징은 논블로킹(Non-Blocking)과 반응형(Reactive) 프로그래밍을 지원하기 위해 Spring Reactive Framework 기반으로 개발됐다는 점이다.

 

WebClient는 이벤트 기반(Event-Driven) 구조를 사용해서, 요청을 보낸 후 응답을 기다리지 않고 다른 작업을 바로 처리할 수 있다.
덕분에 적은 수의 스레드로도 훨씬 더 많은 요청을 동시에 처리할 수 있는 것으로,
Spring WebFlux에 걸맞는 HttpClient이다.

public class MyNonBlockingClient {

    public Mono<String> getNonBlockingData(String url) {
        return WebClient.create()
            .get()
            .uri(url)
            .retrieve()
            .bodyToMono(String.class);
    }
}

 

코드를 보면 RestTemplate와 그렇게 크게 다른 느낌은 들지 않는다.


하지만 이는 비동기식이자 논블로킹 방식이기 때문에 메인 스레드를 막지 않고 여러 요청을 동시에 처리할 수 있다.

 

또한 전형적인 반응형이기 때문에 대용량 데이터를 효율적으로 다루는 것이 가능하다.(filter 등) 그리고 이 과정에서 반응형 스트림을 통해

오류를 전달하기 때문에 오류 처리가 더 간편하다. (onError 시그널을 보내는 등의 형식으로 처리)

 

직렬화/역직렬화에 대하여 WebClient는 Spring에 내장된 기능을 사용해서 별도 라이브러리가 필요 없다는 장점도 존재한다.

 

 


REF