Netty 구조를 파헤쳐보자

Panda·2024년 11월 17일
0

Spring

목록 보기
45/45

요즘 현생이 바빠서 블로그 글을 이제 쓰게되네옄ㅋㅋ

공부 안한건 아니였는데...

Netty의 구조를 파헤쳐보려고 합니다.
TcpCleint 공부하다가 근본까지 공부하게 되었네요

Netty

  • Netty 모든 구성요소에 대한 전체 흐름

  • NIO Web Server
  • 얼핏 보면 Node.js 싱글 스레드 구조랑도 되게 유사하네용

Netty works Based on Multiplexing I/O

  • 하나의 Thread로 여러 개의 Channel을 처리 및 관리하는 것을 Multiplexing I/O 방식입니다.
  • Netty도 이를 채택한 EvnetLoop 기반으로 동작하게 됩니다.

Hello EventLoop

  • 무한 반복문을 실행하며 Selector로부터 I/O 이벤트가 발생할 때까지 대기하다가 이벤트가 발생하면 해당 이벤트를 처리할 수 있는 Handler에게 디스패치합니다.
    보통 특정 Channel에 대한 이벤트를 큐에 삽입할 때, 해당 이벤트를 처리할 수 있는 EventHandler도 같이 첨부해줍니다.

  • EvnetHandler는 이벤트를 받아 비즈니스 로직을 수행한다. (EventHandler가 또다른 Event를 발행할 수 있습니다.)

  • 1 스레드 = 1 EventLoop 입니다.

  • Netty 객체로 NioEventLoop 로 구성이 되어있습니다,

    • 1 Selector + 1 thread + 1 taskQueue

EventLoopGroup

  • Netty의 특징은 EventLoop 를 Group화 시켜 Application을 구성하고 있습니다. (이게 기반이 되는겁니다. 그래서 객체 이름들이 EventLoopGroup 이런식으로 되어있습니다.)

  • Netty는 크게 BossGroup, ChildGroup으로 구분이 되어있습니다.

  • 참고로 WorkerGroup EventLoop 수의 기본값으로는 사용가능 한 프로세서 수 * 2로 설정이되어있습니다. (최솟값 4)

BossGroup

  • Tomcat의 Accept-Count 설정 구간이라고 보시면 됩니다.
  • Acceptor EventLoopGroup이라고 볼 수 있으며, 새로운 클라이언트의 연결 요청을 처리합니다. 즉, 새로운 클라이언트와의 SocketChannel을 생성하고, accept 이벤트를 처리합니다.
  • BossGroup내 EventLoop는 클라이언트와의 연결 부분만 처리하며, accept 완료된 SocketChannel은 ChildGroup내 EventLoop Selector에 위임등록하여 데이터 read/write부터는 ChildGroup내 EventLoop가 처리하도합니다.

ChildGroup (WorkerGroup)

  • Worker EventLoopGroup으로도 많이 알려져있으며, BossGroup으로부터 전달받아 Selector에 등록된 SocketChannel에서 발생하는 read/write 이벤트를 처리합니다.
    read/write에 대한 이벤트를 모두 처리하기때문에 사용자와의 요청/응답에 대한 비즈니스 처리는 모두 ChildGroup내 EventLoop에서 처리합니다.

Channel with ChannelFuture

  • Channel 의 모든 I/O 동작은 비동기로 동작합니다.
  • I/O 수행 상태 및 결과는 ChannelFuture 로 관리합니다.
    • isDone, isSuccess 등 콜백 처리를 합니다.
  • 연결된 EventLoop 조회 (Channel이 등록된 EventLoop를 알 수 있다)

TcpClient 설정중 wireTrap 옵션 걸고 로그를 확인해보면 해당 Channel 상태가 로그로 보여지므로 저는 TcpClient 동작으로 실제 Channel 상태 변화를 확인하였습니다.

ChannelHandler

  • Channel 의 생명주기에 따라 이벤트를 처리하는 ChannelHandler 3가지 종류로 존재합니다.
    • ChannelInboundHandler
    • ChannelOutboundHandler
    • ChannelDuplexHandler
# ChannelInboundHandler Custom 예제

	@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        	ChannelFuture channelFuture = ctx.writeAndFlush(msg);
            // ChannelFuture 객체가 완료 이벤트를 수신할때 채널을 닫음(성공유무 관계없음)		
            channelFuture.addListener(ChannelFutureListener.CLOSE);
            
    }

ChannelPipeline

  • ChannelHandler 들의 Chain 형태가 ChannelPipeline 입니다.
  1. ChannelInitialize 구현이 ServerBootStrap에 등록됩니다.
  2. ChannelInitializer.initChannel()호출시 파이프라인에 ChannelHandler를 추가합니다.
  3. ChannelInitializer는 이후 ChannelPipeline에서 자신을 제거합니다. 아래는 ChannelPipeline의 동작 원리입니다.

번외 지식들

ByteBuffer

  • ByteBuffer는 Kernel의 버퍼를 직접 참조하는 객체입니다.
    • 그렇기 때문에 GC의 대상이 되지않아 메모리누수 문제가 주의가 필요합니다.

Spring Webflux 에서 어떻게 Publisher로 변환할까?

  • Netty 프로젝트로 Reactor Netty로 Publisher로 작동할 수 있게끔 구성이 되어있습니다.
    • 일반적으로 Webflux는 이 Reactor Netty를 사용합니다.
    • 공식 문서

느낀 점

분명 처음에는 단순히 TcpClient 설정만 하려고 했었는데
이 TCP 네트워크 흐름에 대해서 보다갘ㅋ
이 설정 하는법과 TcpClient를 좀 깊숙하게 알아야 할 필요가 있어서 파다보니 근본이 되는 Netty의 구조까지 깊숙하게 공부했네요

요즘에 현업에서 Webflux 기반으로 플젝을 하다보니까 이러한 기본 베이스 공부가 매우 중요하다고 생각합니다.

근데 진짜 이번에 Netty 공부하고 나서
Webflux 설정을 어떻게 해줘야 할지
Tcp 연결 어떻게 해줘야할 지 다 정리가 되어서
진짜 많이 도움이 된 공부였던 것 같습니다.

더 팠으면 OS 레벨까지도 공부할 수도 있긴한데 OS 레벨은 나중으롴ㅋㅋㅋㅋㅋ

참고

profile
실력있는 개발자가 되보자!

0개의 댓글