이번 업무에서는 하나의 게이트웨이(클라이언트)로 다수의 장비에 tcp 소켓으로 접속하여 장비와 통신하는 업무를 맡게 되었다. 여기서 사용하게 된 네트워크 프레임워크로는 Netty를 사용하게 되었다. Netty에 대해 공부한 내용을 정리해 보겠다.
참고 도서는 "자바 네트워크 소녀 Netty" 이다.
1. Netty를 선택하게 된 이유?
기존 HTTP 클라이언트와 같은 범용 프로토콜은 확장성이 좋지 않다. 이는 특별한 목적을 위한 프로토콜을 구현할 때 많은 제약이 있다. Netty는 이러한 문제점을 해결하기 위해 나왔다.
장비와 통신하기 위해 별도의 프로토콜을 정의해서 사용하는데 Netty를 사용하게 되면 특정 목적의 프로토콜을 구현할 때 편리할 것이라고 생각한다.
그럼 Netty에 대해서 알아보자.
2. Netty란?
네티는 비동기 이벤트 기반 네트워크 애플리케이션 프레임워크로써 유지보수를 고려한 고성능 프로토콜 서버와 클라이언트를 개발할 수 있다.
네티는 비동기 이벤트 기반 네트워크 프레임워크이다. 그럼 비동기는 무엇일까?
3. Netty의 주요 특징
동기와 비동기는 여러 의미로 사용되는데, 함수 호출 방식에서의 동기와 비동기에 대해서 알아보자.
a. 동기 / 비동기
동기(Synchronous)란?
함수 A가 함수 B를 호출하면, A는 다른 작업을 하지 않고 B의 결과(리턴값)만을 기다린다.
즉, 함수 A는 함수 B를 호출하고, 함수 B가 작업이 끝나면 다음 작업을 진행할 수 있다.
비동기(Asynchronous )란?
함수 A가 함수 B를 호출할 때, B의 작업 완료 여부를 따지지 않고, A는 다른 작업을 그대로 진행한다.
B가 작업이 끝나면, 콜백함수나, 이벤트를 통해 A에게 작업 결과를 전달하는 방식이다.
* 비동기 호출을 지원하는 옵저버 패턴, 콜백함수, 리액터 패턴 등 다양하다.
비동기는 I/O 작업과 같은 상대적으로 느린 작업을 수행할 때 I/O 작업을 기다리지 않고, 다른 작업을 그대로 진행할 수 있어 동시에 여러 작업을 진행할 수 있다.
예를 들어, 하나의 요청(주문)에 여러 건의 트랜잭션(User 업데이트, 주문처리, 상품재고 처리 등)으로 묶여 있어 DB 쿼리, 로직을 수행하는 데 많은 시간이 걸릴 것이다. 이럴 경우 동기로 처리하면 대규모 트래픽이 발생할 경우 성능이 저하될 수 있다. 비동기 방식으로 처리하면, 응답을 기다리지 않고 웹 애플리케이션은 다른 요청을 처리할 수 있다. 비동기 방식으로 처리하면, 순간적으로 급증하는 트래픽을 처리하기에 매우 적합할 수 있다.
동기와 비동기는 a가 b를 호출할 때 a가 b의 작업 종료 여부에 관심이 있냐, 없느냐의 차이다.
b. 블로킹과 논블로킹
블로킹과 논블로킹을 소켓 I/O 관점에서 살펴보자.
소켓의 기본적인 연결과정이다.
블로킹(Blocking)
블로킹 모드의 소켓은 서버/클라이언트 I/O 작업에서 해당 작업이 완료되기 전까지 스레드의 블로킹이 발생한다. 스레드는 작업이 완료되기 전까지 중단된 채 대기만 하게 된다.
블로킹 소켓은 스레드의 블로킹이 발생하기 때문에 여러 클라이언트의 요청에 대한 처리를 할 수 없다.
그래서 나온 방법이 각각의 클라이언트에게 스레드를 할당하는 모델이다.
accept 메서드가 병목지점이다. accept 메서드는 블로킹 방식으로 동작하여 단위 시간에 하나의 연결만을 처리한다. 즉, 여러 클라이언트가 동시에 접속하는 경우 대기시간이 오래 걸린다.
또한 스레드 생성에 제한이 없기 때문에 메모리 부족으로 인해 OOM 오류가 발생할 수 있다.
메모리 부족 문제는 스레드 풀링으로 해결할 수 있다. 하지만, 접속 가능한 클라이언트 수를 스레드 풀 크기에 의존해야 하며 스레드 풀의 수를 최대로 잡아도, GC 과정에서 수행시간이 오래 걸리며 각 스레드는 CPU 점유를 위해 경쟁(컨텍스트 스위칭)하는 데 이때 CPU 자원을 소모하기 때문에 비효율적이다.
블로킹의 핵심은 스레드가 블로킹되어 다음 작업을 진행할 수 없다!
여기까지만 봐도 블로킹 소켓은 동시접속자를 수용하기에는 적합하지 않다. 블로킹 모드 소켓 단점을 개선한 것이 논블로킹 소켓 방식이다.
논블로킹(Non Blocking)
논블로킹은 즉시 처리할 수 없는 경우라면, 오류를 즉시 리턴되어 호출한 쪽에서 블로킹이 되지 않게 하는 방법이다.
일반적으로 콜이 성공적으로 실행될 때까지 계속 루프를 돌면서 할인하는 방법을 사용한다.
스레드가 블록되지 않기 때문에 멀티스레드를 사용하지 않고도 여러 개의 I/O를 처리할 수 있다.
하지만, 오류 코드를 확인하고 처리하는 부분으로 인해 프로그램 구조가 복잡해질 수 있다.
언뜻 보면 비동기와 논블로킹이 비슷하지만,
비동기는 I/O 처리를 바로 할 수 없을 때 처리가 완료되는 시점까지 백그라운드에서 대기하고, 종료 타이밍을 회신하고,
논블로킹은 I/O 처리가 완료되지 않으면 에러를 회신하여 블록 상태를 만들지 않는다.
블로킹과 논블로킹의 차이는 블로킹은 스레드에서 블로킹되어 여러 개의 I/O를 처리할 수 없고, 논블로킹은 여러개의 단일 스레드에서 여러개의 I/O를 처리할 수 있다!
동기/비동기 vs 블로킹/논블로킹
동기, 비동기는 호출되는 함수의 작업 완료 여부를 신경쓰느냐가 관심사이고, 블로킹, 논블로킹은 호출되는 함수가 바로 리턴하느냐 마느냐가 관심사이다.
c. 이벤트 기반 네트워크 프레임워크
각 이벤트를 정의해 놓고, 이벤트가 발생했을 때 실행되는 코드를 준비해 둔다.
논블로킹 소켓의 Selector를 사용한 I/O 이벤트 감시 역시 이벤트 기반의 프로그램이다.
이벤트 기반 프로그래밍은 추상화 수준이 중요하다. 여기서 추상화란 얼마나 작은 단위로 이벤트를 나눌 것인지 이다.
추상화 수준이 고수준이면 세부적으로 제어가 필요하기 때문에 복잡해지고, 너무 저수준이면 한 동작에 많은 이벤트가 발생하여 성능에 좋지 않을 수 있다.
네트워크 프로그램에서 이벤트는 크게 소켓 연결, 데이터 송수신으로 나눌 수 있으며, Netty에는 다양한 이벤트를 제공한다.
이벤트 기반 네트워크 구조
소켓의 데이터를 읽고 쓸 때, 소켓채널(NIO) 또는 스트림에 기록한다.
Netty는 데이터를 채널에 직접 쓰고 읽지 않으며, 이벤트 핸들러를 통한다.
4. 정리
- 비동기 호출, 논블로킹 소켓을 구현하기에는 코드가 매우 복잡해진다. Netty는 기능 구현에 집중할 수 있도록 프레임워크 레벨에서 API로 제공한다.
- 이벤트 기반의 네트워크 프레임워크로 이벤트에 따라 로직을 분리할 수 있고, 소켓연결, 데이터 송수신, 예외, 연결 종료 등 다양한 이벤트를 제공한다.
'Programming > etc' 카테고리의 다른 글
[Netty] Netty 단위 테스트 작성하기 (0) | 2024.01.17 |
---|---|
[Netty] 바이트버퍼(ByteBuffer) (0) | 2024.01.15 |
[Netty] 이벤트 모델 (0) | 2024.01.15 |
[Netty] 채널 파이프라인과 코덱 (0) | 2024.01.12 |
[Netty] Netty의 Bootstrap(부트스트랩) (0) | 2024.01.12 |