본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로웹 개발 › 백엔드 › Netty

[Netty] 1. 네티 프레임워크

목차
  1. 네티 : 비동기식 이벤트 기반 네트워크 프레임 워크

Netty : 유지 관리가 용이한 고성능 프로토콜 서버와 클라이언트를 신속하게 개발하기 위한 비동기식 이벤트 기반 네트워크 애플리케이션 프레임워크

많은 동시 연결을 다뤄야 할 때, 연결마다 스레드를 만드는 모델과 이벤트 루프 모델의 자원 사용을 비교해 보자. 처리 가능한 연결 수는 메시지 크기·업무 로직·하드웨어에 따라 달라지므로 특정 수치를 보장하지 않는다.

(1) 블로킹 입출력 다중연결

  • 각각 새로운 클라이언트 소켓마다 새로운 스레드를 할당해야함

  • 여러 스레드가 입출력 데이터가 들어오기를 기다리며 무한정 대기 상태로 유지될 수 있다.(리소스낭비)

  • 스레드가 스택 메모리를 할당해야 하는데, 운영체제마다 다르지만 스텍의 기본 크기는 64KB에서 1MB 까지 차지할 수 있다.

  • JVM이 물리적으로 많은 수의 스레드를 지원할 수 있지만, 동시 접속이 한계에 이르기 훨씬 전부터 컨텍스트 전환에 따른 오버헤드가 심각한 문제가 될 수 있다.

(2) 자바 NIO

자바는 블로킹 시스템 호출 방식 외에도 네이티브 소켓 라이브러리에는 오래전 부터 네트워크 리소스 사용률을 세부적으로 제어할 수 있는 논블로킹 호출이 포함돼 있다.

(!) 셀렉터

  • 한 스레드로 여러 소켓을 동시에 연결 처리 할 수 있다.

  • 이 모델은 블로킹 입출력 모델에 비해 전체적으로 훨씬 개선도니 리소스 관리 효율을 보여준다.

  • 적은 수의 스레드로 더 많은 연결을 처리할 수 있으므로, 메모리 관리와 컨텍스트 전환에 따르는 오버헤드가 감소

  • 입출력을 처리하지 않을 때는 스레드를 다른 작업에 활용 가능.

Socket Socket Socket

| | |

R/W R/W R/W

| | |

| | |

----- Selector ---

|

|

Thread

1.1 네티의 특징

설계

  • 단일 API로 블로킹과 논 블로킹 박식의 여러 전송 유형을 지원

  • 단순하지만 강력한 스레딩 모델

  • 진정한 비연결 데이터그램 소켓 지원

  • 재사용 지원을 위한 논리 컴포넌트 연결

이용 편의성

  • 자세한 JavaDoc와 광범위한 예제,

  • 사용하는 Netty 버전과 전송 방식에 맞는 JDK 및 native 의존성을 확인해야 한다.

성능

  • 적절한 파이프라인과 작업 분리 아래에서 적은 스레드로 여러 연결을 처리할 수 있다. 처리량과 지연은 실제 부하로 측정한다.

  • 폴링과 재사용을 통한 리소스 소비 감소

  • 메모리 복사 최소화

견고성

  • 느린 연결과 과부하로 인한 메모리 압박은 여전히 가능하다. 버퍼·큐·프레임 크기에 상한을 둔다.

  • 읽기와 쓰기의 속도가 다르면 쓰기 버퍼가 커질 수 있으므로 채널의 쓰기 가능 상태와 백프레셔를 살핀다.

보안

  • 완벽한 SSL/TLS 및 StartTLS 지원

  • 애플릿이나 OSGI 같은 제한된 환경에서도 이용가능

커뮤니티 주도 개발

  • 자주 릴리즈 됨

1.2.2 비동기식 이벤트 기반 네트워킹

  • 비동기(Asynchronous), 즉 동기화 되지 않은 이벤트

  • 논블로킹 네트워크 연결은 작업 완료를 기다릴 필요가 없게 해줌

  • 완전 비동기 입출력은 이 특징을 바탕으로 한단계 더 나아감

  • 비동기 메서드는 즉시 반환하며 작업이 완료되면 직접 또는 나중에 이를 통지

  • 셀렉터는 적은 수의 스레드로 여러 연결에서 이벤트를 모니터링할 수 있게 해줌

1.3 네티의 핵심 컴포넌트

  • Channel

  • 콜백

  • Future

  • event and handler

1.3.1 channel

  • 하나 이상의 입출력작업을 수행할 수 있는 하드웨어, 장치, 파일, 네트워크 소켓, 프로그램컴포넌트와 같은 엔티티에 대한 열린 연결

  • channel은 들어오는(인바운드)데이터와 나가는(아웃바운드) 데이터를 위한 운송수단이다.

  • channel은 열거나 닫고, 연결하거나 연결을 끊을 수 있다.

1.3.2 콜백

  • 콜백(callback)은 다른 메서드로 자신에 대한 참조를 제공할 수 있는 메서드

  • 다른 메서드에서 이 참조가 가리키는 메서드를 필요할 때 호출할 수 있음

  • 네티에서는 이벤트를 처리할 때 내부적으로 콜백을 이용

  • 콜백이 트리거되면 ChannelHandler 인터페이스의 구현을 통해 이벤트를 처리할 수 있다.

1.3.3 future

  • 작업이 완료되면 이를 애플리케이션에 알리는 방법

  • 비동기 작업의 결과를 담는 placeholder 역할을 함

  • 미래의 어떤 시점에서 작업이 완료되면 그 결과를 접근할 수 있게 해줌

1.3.4. event and handler

  • 네티는 작업의 상태 변화를 알리기 위해 고유한 이벤트를 이용

  • 발생한 이벤트를 기준으로 적절한 동작을 트리거 할 수 있다.

(로깅, 데이터변환, 흐름제어, 어플리케이션 논리)

  • 인바운드 데이터나 연관된 상태 변화로 트리거되는 이벤트

(연결 활성화 or 비활성화, 데이터 읽기, 사용자 이벤트, 오류 이벤트)

  • 아웃바운드 이벤트는 미래에 한 동작을 트리거하는 작업의 결과

(원격 피어로 연결 열기 또는 닫기, 소켓으로 데이터 쓰기 또는 플러시)

연결 하나가 처리되는 순서

클라이언트가 TCP 연결을 만들면 서버 채널이 연결을 수락하고, 새 자식 채널이 worker EventLoop에 등록된다. 채널마다 ChannelPipeline이 있으며, 수신 바이트는 프레임 분리·디코딩을 거쳐 업무 핸들러에 도착한다. 핸들러가 writeAndFlush()를 호출하면 출력 이벤트는 인코더를 거쳐 소켓으로 전송된다.

sequenceDiagram
  participant C as 클라이언트
  participant B as 서버 채널
  participant E as EventLoop
  participant P as ChannelPipeline
  C->>B: TCP 연결 요청
  B->>E: 자식 채널 등록
  C->>P: 바이트 수신
  P->>P: 프레임 분리와 디코딩
  P->>E: 업무 핸들러 호출
  E->>P: writeAndFlush 응답
  P-->>C: 인코딩된 바이트

이 흐름에서 ChannelFuture는 비동기 작업의 완료 또는 실패를 확인하는 수단이다. 예를 들어 bind()의 반환값을 확인하지 않으면 서버가 포트를 열었다고 잘못 판단할 수 있다. 반대로 EventLoop 안에서 future.sync()처럼 완료를 기다리는 동작을 반복하면 그 EventLoop가 맡은 다른 채널 처리도 막힌다. 부팅 코드에서 기다리는 것과 I/O 핸들러 안에서 기다리는 것은 맥락이 다르다.

ByteBuf와 처리량의 실제 제약

수신 데이터는 흔히 ByteBuf로 전달된다. Netty의 버퍼는 참조 카운트로 관리되므로 마지막 소비자가 해제하거나 다음 핸들러로 소유권을 넘겨야 한다. 두 쪽 모두 해제하면 이미 해제된 버퍼를 사용하고, 아무도 해제하지 않으면 메모리가 샌다. SimpleChannelInboundHandler는 소비한 메시지의 자동 해제를 제공하지만, 메시지를 비동기 작업에 보관한다면 그 수명 규칙을 다시 확인한다.

연결이 많다는 사실만으로 서버가 안정적이지는 않다. 클라이언트가 천천히 읽으면 서버의 쓰기 버퍼가 커지고, 길이 제한 없는 프레임은 큰 메시지로 메모리를 채울 수 있다. EventLoop에서 느린 JDBC 호출을 직접 실행하면 네트워크 이벤트까지 지연된다. 따라서 한 연결당 입력 프레임 최대 크기, 읽기 타임아웃, 처리 큐 길이, 쓰기 가능 상태를 정하고 부하 테스트에서 실제 메모리·지연을 본다.

보이는 현상먼저 볼 곳
연결은 됐지만 응답이 없음프레임 경계, flush(), 핸들러 이벤트 전파
연결 수가 늘 때 지연 급증EventLoop의 블로킹 작업, 작업 큐, CPU
direct memory 증가ByteBuf 해제와 쓰기 버퍼
일부 메시지가 깨짐TCP 조각을 한 메시지로 가정했는지
서버 시작 성공처럼 보이지만 접속 불가bind() 성공 여부와 실제 바인딩 주소

처음 구현할 때는 작은 에코 서버에서 bind(), 파이프라인, 프레임 경계를 확인한 뒤 업무 코드를 넣는다. NIO·epoll 중 무엇이 빠른지 먼저 가정하기보다 동일한 프로토콜과 부하에서 비교한다.

참고: Netty 4.x 사용자 가이드, Netty 참조 카운트 설명.

같은 카테고리의 글