Network

[Java] Netty (Concept, Architecture, Component, Work flow)

SeungbeomKim 2024. 6. 27. 18:28

 

오늘은 Java 기반 비동기 이벤트 중심의 네트워크 프레임워크인 Netty에 대해서 알아보려고 합니다. 회사에서 WebSocket 통신을 위해 Netty를 도입했지만, 막연히 소켓 통신을 위한 프레임워크 정도로만 인지하고 있었고 세부적인 내용은 모른 상태였습니다.

 

저는 Netty를 통해 네트워크 소켓 통신이 어떻게 이루어지고, 아키텍처와 동작과정에 대해 분석할 겸 하나씩 정리해보려고 합니다. (물론 웹소켓이 아닌 다양한 많은 프로토콜을 구축할 수 있습니다) 

 

Netty를 개념과 다양한 컴포넌트, 장점(Blocking I/O 방식과 차이를 곁들여서) 및 사용 목적에 대해 상세히 설명드리겠습니다.

 

Netty

  • 자바 기반의 비동기 이벤트 중심의 네트워크 애플리케이션 프레임워크
  • 유지 관리가 용이한 고성능 프로토콜 서버와 클라이언트를 신속하게 개발하기 위한 비동기식 이벤트 기반 네트워크 애플리케이션 프레임워크
  • low level의 API지만, 다양한 플랫폼, 웹 프레임워크에 사용 (Apple, X, Fracbook, Spring WebFlex, Elasticsearch ..)

 

사용 목적

  • 네트워크와 비즈니스 로직 구성 요소를 분리하고 느슨하게 결합하여 NIO 기반의 고성능 프로토콜 서버 구축
  • HTTP와 같이 널리 알려진 프로토콜이나 사용자가 정의한 프로토콜 구현이 가능

 

Netty Event

  • Inbound Event(network -> server)
    • methods: channelRegisterd, channelUnregistered, channelActive, channelInactive, channelRead
    • 채널 활성화 및 비활성화, 작업 이벤트 읽기, 예외 이벤트
  • Outbound Event(server -> network)
    • methods: bind, connect, disconnect, close, deregister, read, write, flush
    • 로컬 주소 바인딩 요청, 원격 피어로 연결 요청, 연결 해제, 채널 종료, 데이터 읽기 및 쓰기

 

Netty는 Non blocking Framework인데, 이로 인해서 blocking-IO에 비해 처리량이 높아지고 더 많은 이벤트를 빠르고 효율적으로 처리할 수 있습니다.

  • 하나의 스레드로 여러 동시 연결을 처리할 수 있습니다.
  • 셀렉터는 적은 수의 스레드로 여러 연결에서 이벤트를 모니터링할 수 있게 해 줍니다.

Selector

Channel에서의 모든 작업은 비동기로 실행되기에 호출 즉시 반환됩니다. 자바에서 Future (작업이 완료되면 이를 애플리케이션에 알리는 한 방법) interface를 제공하지만, Netty 목적에 부합하지 않습니다. 왜냐하면 수동으로 작업완료 여부를 확인하거나 완료되기 전까지 블로킹하는 기능만 있기 때문입니다.

 

그래서 기존 Java에서 제공하는 Future와는 다르게, Netty는 독립적인 ChannelFuture interface를 가집니다. 작업 완료 시 호출되는 ChannelFuture에 callback을 전달합니다.

 

이제 대략적인 개념과 특징에 대해 알아봤으니, 해당 컴포넌트와 Pipeline에 대해 설명드리겠습니다.

 

Netty의 Component

  • Channel
    • 읽기, 쓰기, 연결, 바인드 등의 I/O 작업을 할 수 있는 요소 또는 네트워크 연결
    • 모든 I/O 작업은 비동기 → ChannelFuture
  • ChannelFuture
    • Channel의 I/O 작업의 결과
    • ChannelFutureListener를 등록 결과에 따른 작업
  • ChannelHandler
    • Netty의 핵심 요소이자 I/O 이벤트를 처리하는 인터페이스
    • ChannelInboundHandlerAdapter, ChannelOutboundHandlerAdapter

하나의 Channel에서의 단편적인 Pipeline

  • ChannelHandlerContext
    • channel에 대한 입출력을 처리 (writeAndFlush: 채널에 data 기록, close: 채널의 연결을 종료, exceptioncaught: 예외 처리 이벤트)
    • ChannelHandler는 ChannelHandlerContext를 통해 다음 ChannelHandler에게 이벤트를 넘기거나 동적으로 ChannelPipeline을 변경할 수 있습니다
  • ChannelPipeline
    • Channel에 드나드는 inbound/outbound 이벤트를 처리
    • 모든 Channel에는 자체적인 ChannelPipeline이 포함되어 있습니다 (위의 사진에서 봤듯이)
    • ChannelHandler의 List (ChannelInboundHandler, ChannelOutboundHandler)
  • decoder
    • InboundHandler는 Socket에서 ByteBuf를 가져옵니다
    • Netty로 들어오는 data(byteBuf)를 java 객체로 디코딩 (default class: ByteToMessageDecoder)
  • encoder
    • OutBoundHandler는 소켓에 쓸 ByteBuf 생성합니다
    • Netty에서 나가는 data를 인코딩 (default class: MessageToByteEncoder)

  •  EventLoop
    • 등록된 Channel들의 모든 I/O 작업을 처리 (하나의 Thread로 바인딩되며 제어흐름, 멀티스레딩, 동시성 제어 등의 기능 수행)
    • 구현체는 주로 NioEventLoopGroup을 주로 사용
    • 각각의 eventloop는 단일 스레드에 의해 구동되기에 작업의 순서를 보장합니다 (Class Diagram 참조)

Eventloop Class Diagram

  • EventLoopGroup
    • 단일 Thread와 독점적으로 연결됩니다.
    • EventLoop들을 포함하는 Group

  • Bootstrap
    • netty로 작성한 애플리케이션이 시작 때 가장 처음 수행되는 요소
    • group: 이벤트 루프 설정
      • ServerBootStrap: 연결수락 이벤트루프, 데이터 송수신처리 이벤트루프 등록 
      • ClientBootStrap: 데이터 송수신처리 이벤트 루프 등록
    • channel(): 소켓 입출력 모드 설정
      • LocalServerSocketChannel.class: 하나의 자바 가상머신에서 가상 통신을 위한 서버 소켓 채널을 생성하는 클래스
      • OioServerSocketChannel.class: 블로킹 모드의 서버 소켓 채널을 생성하는 클래스
      • NioServerSocketChannel.class: 논블로킹 모드의 서버 소켓 채널을 생성하는 클래스
      • EpollServerSocketChannel.class: 리눅스 커널의 소켓 채널을 생성하는 클래스
    • childHandler(): 서버에 연결된 소켓 채널에서 발생하는 이벤트 수신 및 처리
      • TCP_NODELAY: 데이터 송수신에 Nagle 알고리즘 비활성화 여부 지정
      • SO_KEEPALIVE: 운영체제에서 지정된 시간에 한 번씩 keepalive 패킷을 상대방에게 전송

 

Netty Work Flow

  • BootStrap -> EventLoopGroup -> EventLoop -> SocketChannel -> ChannelPipeline -> ChannelHandler

Netty work flow

  • Boss Group을 통해 승인된 채널들이 Worker Group으로 전달합니다.
  • Worker Group은 해당된 채널에 따라 Event Loop가 동작되는데, 각각의 EventQueue를 가지고 있어 Event의 순서를 보장합니다.
  • 해당되는 Channel들은 PipeLine에 구축된 ChannelHandler들의 체이닝에 따라 동작이 진행됩니다. 

 

ChannelPipeLine Example

  • ChannelPipeline, ChannelHandler chain은 Netty 기반 애플리케이션 또는 드라이버의 핵심
  • Netty는 TLS처리, HTTP 인코딩/디코딩, WebSocket 프로토콜 구현 등 개발을 단순화하기 위해 ChannelHandler 구현체 제공
  • Socket에서 들어오는 요청을 처리하는 ChannelInBoundHandler, 응답을 전달하는 ChannelOutBoundHandler로 구성하고, WebSocket 프로토콜만 구현할 경우에는 PipeLine을 단순화할 수 있습니다.

이제 대략적인 아키텍처와 동작과정은 파악했으니, 다음 포스팅에는 Netty를 이용하여 간단한 HTTP Server를 구축해보도록 하겠습니다.

 

 

<참고 자료>

https://medium.com/@akhaku/netty-data-model-threading-and-gotchas-cab820e4815a

 

Netty data model, threading, and gotchas

An overview of Netty basics and some best practices to avoid shooting yourself in the foot.

medium.com

https://rahs.tistory.com/200

 

네티 4.x 유저 가이드(Netty Uesr guide for 4.x)

Netty User guide for 4.x를 옮기며...[최종 수정 일자 : 2018년 2월 26일] 문서 볼륨이 크지 않고, 이해가 필수적인 부분에 대해 굉장히 잘 설명되어 있어서 옮겨봅니다. 오역은 없을 것이라 생각됩니다만

rahs.tistory.com