Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Tags more
Archives
Today
Total
관리 메뉴

ultra_dev

Netty 프레임워크 기본 [공식 문서 참조] 본문

SPRING&JAVA

Netty 프레임워크 기본 [공식 문서 참조]

ultra_dev 2024. 7. 5. 18:29

○ 목차

  1. Netty란
  2. 이벤트루프와 소켓 생성 흐름
  3. Basic 예제
  4. Discard 예제
  5. Echo 예제
  6. Time 예제
  7. LifeCycle 예제
  8. Duplexhandler

프로젝트

  1. 프로젝트 구조

Netty란

  1. Netty는 비동기 이벤트 기반 네트워크 애플리케이션 프레임워크로, 빠르고 신뢰성 있는 네트워크 서버 및 클라이언트를 쉽게 개발할 수 있게 도와줌
  2. 비동기식 네트워크 통신을 지원하며, Java NIO를 기반으로 구축되었으며 주요 특징으로는 높은 성능, 간편한 사용성, 강력한 유연성 등이 있음

사용이유

  1. 현재 회사에서 자율주행차량 관련 TCP-Gateway 프로젝트에서 효율적인 개발을 위해 사용
  2. Byte 단위로 데이터를 주고받기 때문에 네트워크 프로토콜의 효율적인 처리와 사용자 정의 데이터 포맷을 손쉽게 구현할 수 있음
  3. 비동기 논블로킹 방식으로 동작해 다수의 연결을 효율적으로 처리하고, 리소스 사용을 최소화할 수 있음

○ 이벤트 루프란?

  • 이벤트 루프(Event Loop):
    • 싱글 스레드 기반 비동기 I/O 처리 루프
    • 연결 요청, 데이터 읽기/쓰기, 이벤트 핸들링 등의 작업을 비동기적으로 처리
  • 한 이벤트 루프는 다수의 채널(Channel)을 관리
    • 한 채널은 항상 동일한 이벤트 루프에 바인딩되어 처리됨
    • 이를 통해 스레드 경쟁을 방지하고, 높은 성능과 안정성을 제공
  • 이벤트 루프 그룹(EventLoopGroup)
    • Netty는 작업의 성격에 따라 이벤트 루프를 그룹으로 관리. 주요 이벤트 루프 그룹은 다음과 같음
    1. BossGroup
      1. 서버 소켓(ServerSocketChannel)에서 클라이언트의 연결 요청(Accept)을 처리
      2. 연결 요청이 수락되면 작업을 WorkerGroup으로 위임
      3. 일반적으로 스레드 1개로 충분
    2. WorkerGroup
      1. 클라이언트와의 데이터 송수신 및 이벤트 처리를 담당
      2. WorkerGroup은 여러 스레드를 사용하며, 각 스레드는 다수의 클라이언트를 비동기적으로 처리
  • 이벤트 루프 주요 특징
    • 싱글 스레드 모델
      1. 이벤트 루프는 1개의 스레드로 작동하며, 각 채널에 전용으로 할당
      2. 한 채널의 모든 작업(I/O, 이벤트 핸들링 등)은 동일한 스레드에서 처리
    • 다중 채널 관리
      1. 각 이벤트 루프는 다수의 채널을 비동기로 처리
      2. 네트워크 부하가 크지 않다면 스레드 1개로 수백~수천 개의 채널을 관리할 수 있음
    • 비동기 및 논블로킹 처리
      1. Netty는 작업을 비동기적으로 처리하며, 논블로킹 방식으로 네트워크 I/O를 수행
      2. 작업의 결과는 ChannelFuture 객체를 통해 비동기적으로 확인 가능

○ 소켓 생성 흐름 간략화

  • Netty에서 소켓 생성은 ServerBootstrapBootstrap을 통해 이루어지며 흐름은 아래와 같음
  1. 서버 소켓 생성
    • ServerBootstrap을 통해 서버 소켓 생성
    • 특정 포트에 바인딩 되어 클라이언트의 연결 요청 대기
    • 이 소켓을 "서버 소켓 A"이라고 명명
      • 서버 소켓 A는 여러 클라이언트의 연결 요청을 처리하는 수신용 소켓으로 데이터 통신에 관여하지 않음
      • 서버 소켓 A는 BossGroup에 속하며, BossGroup의 이벤트 루프가 관리
  2. 클라이언트 소켓 생성
    • Bootstrap을 통해 클라이언트 소켓 생성
    • 서버의 IP 주소와 포트로 연결 요청 전송
    • 이 소켓을 "클라이언트 소켓 B"이라고 명명
  3. 연결 요청 및 수락
    • "클라이언트 소켓 B"가 "서버 소켓 A"에 연결 요청 전송
    • BossGroup에 속한 이벤트 루프가 이 연결 요청을 처리하며, 요청이 수락되면 서버는 WorkerGroup으로 작업을 위임
    • 따라서, "서버 소켓 A"가 연결 요청 수락한 뒤, 새로운 채널 생성
  4. 새로운 소켓 생성
    • 연결이 수락 되면, 서버는 새로운 소켓 생성
    • 이 소켓을 "서버 소켓 C"라고 명명
    • "서버 소켓 C"는 WorkerGroup의 한 이벤트 루프(스레드)에 바인딩되어, "클라이언트 소켓 B"와 직접 통신하며 데이터 송수신 작업 처리
    • "서버 소켓 A"는 여전히 다른 클라이언트의 연결 요청 대기

01_Basic 예제

  1. 목표
    • Netty를 사용하여 간단한 클라이언트-서버 애플리케이션을 구축
  1. 관련 코드
    • 서버 BasicServer Class

  • 서버 ServerHandler Class

  • 클라이언트 BasicClient Class

  • 클라이언트 ClientHandler Class

  1. 주요 구성 요소
    • EventLoopGroup: I/O 작업을 처리하는 스레드 풀
    • Bootstrap: 클라이언트 설정을 위한 도우미 클래스
    • ServerBootstrap: 서버 설정을 위한 도우미 클래스
    • Channel: 네트워크 소켓을 나타내며 데이터의 입출력을 처리
    • ChannelInboundHandlerAdapter: 수신된 데이터를 처리하는 기본 핸들러
  2. 코드 설명
    • 서버: 클라이언트의 연결을 수락하고 연결 상태를 출력
      • ServerBootstrap을 통해 서버 설정 및 클라이언트 연결 요청 대기
      • ServerHandler의 channelActive 메소드에서 클라이언트 연결 시 클라이언트 주소 출력
    • 클라이언트: 서버에 연결하고 연결 상태를 출력
      • Bootstrap을 통해 클라이언트 설정 및 서버 연결 요청
      • ClientHandler의 channelActive 메소드에서 서버 연결 시 상태 출력
  3. 결과
  • BasicServer
  • BasicClient

02_Discard 예제

  1. 목표
    • Discard 서버는 수신한 데이터를 처리하지 않고 버리는 서버로, 이를 통해 Netty의 데이터 흐름의 기본 개념을 이해
  2. 관련 코드
클라이언트

클라이언트 DiscardClientHandler Class

서버

서버 DiscardServerHandler Class

  1. 코드 설명
    • 서버 : 수신된 데이터를 단순히 버림
      • ServerBootstrap을 통해 서버 설정 및 클라이언트 연결 요청 대기
      • DiscardServerHandler의 channelRead0 메소드에서 수신된 데이터를 읽지만 처리하지 않음 (무시)
    • 클라이언트 : 서버에 데이터를 전송
      • Bootstrap을 통해 클라이언트 설정 및 서버 연결 요청
      • DiscardClientHandler의 channelActive 메소드에서 서버에 데이터를 전송
  2. 결과 : Basic 예제와 상동

03_Echo 예제

  1. 목표
    • Echo 서버는 클라이언트로부터 받은 메시지를 그대로 다시 클라이언트에 전송하는 서버로, 이를 통해 Netty의 메시지 처리 방식의 기본 개념을 이해
  2. 관련 코드
클라이언트

- 클라이언트 EchoClientHandler Class
- 클라이언트 EchoClientHandler Class

서버

서버 EchoServerHandler Class

3. 코드 설명

  • 서버 : 클라이언트로부터 수신된 메시지를 그대로 다시 클라이언트로 전송
    • ServerBootstrap을 통해 서버 설정 및 클라이언트 연결 요청 대기
    • EchoServerHandler의 channelRead 메소드에서 클라이언트로부터 수신된 메시지를 다시 클라이언트로 전송
  • 클라이언트 : 서버에 메시지를 보내고, 서버로부터 받은 메시지를 출력
    • Bootstrap을 통해 클라이언트 설정 및 서버 연결 요청
    • EchoClientHandler의 channelActive 메소드에서 서버에 데이터를 전송
    • EchoClientHandler의 channelRead 메소드에서 서버로부터 수신된 메시지를 출력
  1. 결과
  • EchoServer

  • EchoClient

04_Time 예제

  1. 목표
  • 인코더(TimeEncoder)와 디코더(TimeDecoder)를 통해 데이터를 효율적으로 변환하여 주고받는 과정을 통해 Netty에서 데이터 인코딩과 디코딩 방식의 개념을 이해
  1. 관련 코드
  • 클라이언트 TimeClient Class

  • 클라이언트 TimeClientHandler Class

  • 서버 TimeServer Class

  • 서버 TimeServerHandler Class

  • 인코더 TimeEncoder Class

  • 디코더 TimeDecoder Class

  • POJO UnixTime Class

  1. 코드 설명
  • 서버 : 클라이언트가 접속하면 현재 시간을 전송
    • ServerBootstrap을 통해 서버 설정 및 클라이언트 연결 요청 대기
    • TimeServerHandler의 channelActive 메소드에서 클라이언트가 접속하면 현재 시간을 전송
    • TimeEncoder를 통해 데이터를 UnixTime 객체로 인코딩하여 전송
  • 클라이언트 : 서버로부터 시간을 받아 출력
    • Bootstrap을 통해 클라이언트 설정 및 서버 연결 요청
    • TimeClientHandler의 channelRead 메소드에서 서버로부터 수신된 시간을 디코딩하여 출력
    • TimeDecoder를 통해 데이터를 UnixTime 객체로 디코딩하여 처리

05_LifeCycle 예제

  1. 목표
  • 각 핸들러가 순서대로 등록, 해제, 활성화, 비활성화 되는 과정을 보면서, Netty에서 핸들러의 라이프 사이클과 이벤트 흐름을 이해
  1. 관련 코드
  • InboundFirst Class

  • OutboundFirst Class

  • 서버 LifeCycleServer Class

  1. 코드 설명
    • 서버 : 여러 인바운드 및 아웃바운드 핸들러를 통해 메시지가 처리되는 과정을 로깅
      • ServerBootstrap을 통해 서버 설정 및 클라이언트 연결 요청 대기
      • InboundFirst와 InboundSecond 핸들러를 통해 연결 상태와 수신 된 메시지 로깅
      • OutBoundFirst와 OutBoundSecond 핸들러를 통해 송신 되는 메시지 로깅
    • 클라이언트 : 서버에 메시지를 보내고 핸들러의 각 단계에서 상태를 로깅
      • Bootstrap을 통해 클라이언트 설정 및 서버 연결 요청
      • LifeCycleClientHandler를 통해 서버로 메시지 전송 및 각 단계에서 상태 로깅
  2. LifeCycle
  • 연결 : 핸들러 파이프 라인 등록 → 이벤트 루프 등록 → 채널 활성화

  • 해제 : 채널 비활성화 → 이벤트 루프 제거 → 핸들러 파이프 라인 제거

  • 메시지 송수신
  • 파이프 라인 추가 순서
    • InboundHandler (First → Second)
    • OutBoundHandler (First → Second)
  • 메시지 수신
    • InboundHandler의 경우 Bottom-up 방식으로 진행
    • 채널 파이프 라인의 맨 아래에 있는 핸들러부터 시작하여 맨 위에 있는 핸들러로 메시지 전달
    • 위 순서로 등록된 핸들러에서 메시지 수신 시, InboundFirst → InboundSecond 순서로 처리
  • 메시지 송신
    • OutboundHandler의 경우 Top-down 방식으로 진행
    • 채널 파이프라인의 맨 위에 있는 핸들러부터 시작하여 맨 아래에 있는 핸들러로 메시지 전달
    • 위 순서로 등록된 핸들러에서 메시지 송신 시, OutboundSecond → OutboundFirst 순서로 처리
  • 결과
    • 메시지 수신 : InboundFirst → InboundSecond
    • 메시지 송신 : OutboundSecond → OutboundFirst

핸들러(인바운드, 아웃바운드, 듀플렉스 비교)

  1. 인바운드 핸들러 (ChannelInboundHandler)
    • 역할: 수신된 데이터를 처리
    • 주요 메서드
      • channelRead(ChannelHandlerContext ctx, Object msg): 수신된 메시지 처리
      • channelActive(ChannelHandlerContext ctx): 채널이 활성화될 때 호출
      • channelInactive(ChannelHandlerContext ctx): 채널이 비활성화될 때 호출
  2. 아웃바운드 핸들러 (ChannelOutboundHandler)
    • 역할: 송신할 데이터를 처리
    • 주요 메서드
      • write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise): 송신할 메시지 처리
      • flush(ChannelHandlerContext ctx): 대기 중인 데이터를 네트워크로 전송
  3. 듀플렉스 핸들러 (ChannelDuplexHandler)
    • 역할: 인바운드와 아웃바운드 이벤트를 모두 처리
    • 주요 메서드
      • 인바운드 메서드: channelRead, channelActive, channelInactive 등
      • 아웃바운드 메서드: write, flush 등
    • 특징: 인바운드와 아웃바운드 핸들러의 기능을 결합하여 단일 핸들러에서 양방향 데이터를 처리
  • 사용 이유
    • 코드 단순화: 인바운드와 아웃바운드 이벤트를 동일한 논리로 처리해야 하는 경우, 두 핸들러를 별도로 작성하는 것보다 단일 핸들러에서 관리하는 것이 코드 작성과 관리가 용이
    • 유지보수성: 동일한 핸들러에서 양방향 이벤트를 처리함으로써 관련된 로직이 한 곳에 집중되므로 유지보수가 용이
    • 상태 관리: 인바운드와 아웃바운드 처리 간에 공유 상태가 필요한 경우, 듀플렉스 핸들러를 사용하면 상태 관리가 쉬움
  • 대표 사례
    • 로깅 핸들러: 메시지의 수신과 송신을 모두 로깅해야 하는 경우, 듀플렉스 핸들러를 사용하여 단일 클래스에서 로깅 로직을 관리
    • 트래픽 모니터링: 네트워크 트래픽을 모니터링하여 수신 및 송신 데이터를 분석하는 경우, 듀플렉스 핸들러를 통해 인바운드와 아웃바운드 트래픽을 동시에 처리 가능
    • 데이터 변환: 특정 프로토콜에서 데이터를 수신하여 변환한 후 다른 프로토콜로 송신해야 하는 경우, 듀플렉스 핸들러를 사용하여 변환 로직을 통합할 수 있음
  • 듀플렉스 핸들러 예시 코드
    • 이 핸들러는 ChannelDuplexHandler를 상속받아 인바운드와 아웃바운드 이벤트를 모두 처리
      1. channelRead 메서드에서는 수신된 메시지를 로그에 기록한 후 다음 핸들러로 전달
      2. write 메서드에서는 송신할 메시지를 로그에 기록한 후 다음 아웃바운드 핸들러로 전달
    • 이와 같이 ChannelDuplexHandler를 사용하면 인바운드와 아웃바운드 이벤트를 단일 클래스에서 처리할 수 있어 관련 로직을 통합하고 관리할 수 있음

  • 듀플렉스 핸들러 메시지 송수신 순서 결과
    • 연결 순서

1.  InboundFirst
2.  OutBoundFirst
3.  DupldexFirst
4.  OutboundSecond
5.  DuplexSecond
6.  InboundSecond
  • 결과
    • 아래와 같이 메시지 수신은 인바운드 방식을 따라서 Bottom-up, 메시지 송신은 아웃바운드 방식을 따라서 Top-down 방식으로 진행됨을 알 수 있음

  • 메시지 수신 순서(Bottom-up)
    1. InboundFirst
    2. DuplexFirst
    3. DuplexSecond
    4. InboundSecond
  • 메시지 송신 순서 (Top-down)
    1. DuplexSecond
    2. OutboundSecond
    3. DuplexFirst
    4. OutboundFirst
Comments