본문 바로가기
Spring & Spring Boot

Netty

by 지민재 2023. 11. 21.
반응형
SMALL

Netty(네티)는 네트워크 응용 프로그램을 개발하기 위한 고성능, 이벤트 기반의 네트워크 프레임워크입니다. Netty는 Java로 작성되었으며, TCP, UDP, HTTP 및 기타 다양한 프로토콜을 지원하는 다목적 네트워크 프로그래밍을 위해 설계되었습니다. 여기에는 다양한 기능이 포함되어 있어 대규모의 분산 시스템 및 서버 개발에 적합합니다.

 

 


Maven 프로젝트에서 pom.xml 파일에 의존성 dependency 추가  

<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.86.Final</version>
</dependency>

 

Netty 구조

 

application.yml

netty:
  server:
    use: true
    port: 2xxx
  client:
    use: true
    host: 1xx.xxx.xx.xx
    port: 3xxx
    requestTime: 10

application.yml 파일에 서버와 클라이언트에 대한 정보를 입력해줍니다. 

 

Netty의 구조

  1. Channel 및 ChannelPipeline: 네트워크 통신은 Channel을 통해 이루어지며, 각 Channel에는 연결된 파이프라인(ChannelPipeline)이 있습니다. ChannelPipeline은 이벤트 핸들러의 체인으로 구성되어 있습니다.
  2. EventLoop 및 EventLoopGroup: EventLoop는 비동기 이벤트 처리를 담당하는 스레드입니다. EventLoopGroup은 여러 EventLoop를 관리하며, 주로 서버와 클라이언트의 이벤트 처리를 담당합니다.
  3. ChannelHandler: 이벤트 핸들러는 데이터 수신, 쓰기, 예외 처리 등의 이벤트를 처리하는 데 사용됩니다. ChannelInboundHandler 및 ChannelOutboundHandler는 각각 입력 및 출력 이벤트를 처리합니다.
  4. ChannelFuture: 비동기 작업의 결과를 나타냅니다. ChannelFuture는 비동기 작업이 완료되었을 때 애플리케이션에 알리기 위해 사용됩니다.

Netty는 이러한 구조를 통해 성능, 확장성, 유연성을 제공하며, 다양한 네트워크 애플리케이션을 쉽게 개발할 수 있도록 도와줍니다.

 

NettyClientManager

@Component
public class NettyClientManager {
    private static final Logger logger = LoggerFactory.getLogger(NettyClientManager.class);
    private static NettyClientManager instance = null;

    @Value("${netty.client.host}")
    private String host;
    @Value("${netty.client.port}")
    private int port;
    @Value("${netty.client.requestTime}")
    private int connectionRequestTime = 0;
    @Value("${netty.client.use}")
    private boolean useFlag;

    private boolean nettyConnectionStatus = false;
    private ChannelHandlerContext channelHandlerContext = null;
    private final EventLoopGroup workerGroup = new NioEventLoopGroup();
    private Bootstrap bootstrap = new Bootstrap();
    private final ClientConnectionProcess clientConnectionProcess;
    private final SendDataProcess sendDataProcess;
    private final ReceiveDataProcess receiveDataProcess;
    private Thread connectionThread;
    private Thread receiveDataThread;
    private Thread sendDataThread;

    public NettyClientManager(@Qualifier("NettyClientConnectionProcess") ClientConnectionProcess clientConnectionProcess,
                              @Qualifier("NettyClientSendDataProcess") SendDataProcess sendDataProcess,
                              @Qualifier("NettyClientReceiveDataProcess") ReceiveDataProcess receiveDataProcess) {
        this.clientConnectionProcess = clientConnectionProcess;
        this.sendDataProcess = sendDataProcess;
        this.receiveDataProcess = receiveDataProcess;
    }

    public static NettyClientManager getInstance() {
        return instance;
    }

    public int getConnectionRequestTime() {
        return connectionRequestTime;
    }

    public void setNettyConnectionStatus(boolean nettyConnectionStatus) {
        this.nettyConnectionStatus = nettyConnectionStatus;
    }

    public boolean isNettyConnectionStatus() {
        return nettyConnectionStatus;
    }

    public ChannelHandlerContext getChannelHandlerContext() {
        return channelHandlerContext;
    }

    public void setChannelHandlerContext(ChannelHandlerContext channelHandlerContext) {
        this.channelHandlerContext = channelHandlerContext;
    }

    @PostConstruct
    void initialize() {
        instance = this;

        clientConfigSet();

        if (useFlag) {
            this.connectionThread = new Thread(clientConnectionProcess);
            this.sendDataThread = new Thread(sendDataProcess);
            this.receiveDataThread = new Thread(receiveDataProcess);

            this.connectionThread.start();
            this.sendDataThread.start();
            this.receiveDataThread.start();
        }
    }

    @PreDestroy
    void destroy() {
        this.connectionThread.interrupt();
        this.receiveDataThread.interrupt();
        this.sendDataThread.interrupt();

        this.workerGroup.shutdownGracefully();
    }

    private void clientConfigSet() {
        bootstrap = new Bootstrap();
        bootstrap.group(workerGroup);
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) {
                ChannelPipeline pipeline = ch.pipeline();
                pipeline.addLast(new ByteArrayDecoder());
                pipeline.addLast(new ByteArrayEncoder());
                ch.pipeline().addLast(new SocketClientHandler());
            }
        });
    }

    public void connect() throws InterruptedException {
        ChannelFuture f = bootstrap.connect(host, port).sync();
        f.channel().closeFuture().sync();
    }

    public void receiveDataEnqueue(NettyClientDataContext data) {
        this.receiveDataProcess.receiveDataEnqueue(data);
    }

    public void sendDataEnqueue(byte[] sendData) throws NettyClientException {
        NettyClientDataContext nettyClientDataContext = new NettyClientDataContext();
        nettyClientDataContext.setOriginalData(sendData);

        this.sendDataProcess.sendDataEnqueue(nettyClientDataContext);
    }
}
  • 이 코드는 Netty를 사용하여 클라이언트 측에서 네트워크 통신을 관리하는 NettyClientManager 클래스입니다.  @Value 어노테이션으로 스프링의 설정 파일(application.yml 혹은 application.properties)을 읽어 옵니다.

필드 및 속성

  • host, port: 서버에 연결할 호스트와 포트 정보를 저장하는 필드입니다.
  • connectionRequestTime: 연결 요청 시도 간격을 지정하는데 사용되는 시간입니다.
  • useFlag: Netty를 사용할지 여부를 나타내는 플래그입니다.
  • nettyConnectionStatus: Netty 연결 상태를 나타내는 플래그입니다.
  • channelHandlerContext: Netty Channel의 컨텍스트를 저장합니다.
  • workerGroup: Netty의 이벤트 루프 그룹으로, 클라이언트의 이벤트 처리를 담당합니다.
  • bootstrap: 클라이언트를 설정하고 부트스트랩하는 데 사용되는 Netty의 부트스트랩 객체입니다.
  • clientConnectionProcess, sendDataProcess, receiveDataProcess: 각각 연결, 데이터 전송, 데이터 수신을 처리하는 프로세스입니다.
  • connectionThread, receiveDataThread, sendDataThread: 각각 연결, 데이터 수신, 데이터 전송을 처리하는 스레드입니다.

생성자

  • 생성자에서는 연결, 데이터 전송, 데이터 수신을 처리하는 프로세스를 주입받습니다.

메서드

  • initialize: 객체 초기화를 위한 메서드로, 스레드를 생성하고 시작합니다.
  • destroy: 객체 소멸을 위한 메서드로, 스레드를 종료하고 Netty의 이벤트 루프 그룹을 정리합니다.
  • clientConfigSet: Netty 클라이언트의 구성을 설정합니다. Bootstrap 객체를 초기화하고, 채널 초기화, 디코더 및 인코더를 설정합니다.
  • connect: 서버에 연결하는 메서드로, 지정된 호스트와 포트로 연결을 시도하고 대기합니다.
  • receiveDataEnqueue: 데이터를 수신하는 메서드로, 수신된 데이터를 처리하는 프로세스에 큐에 넣습니다.
  • sendDataEnqueue: 데이터를 전송하는 메서드로, 전송할 데이터를 처리하는 프로세스에 큐에 넣습니다.

@PostConstruct 및 @PreDestroy 어노테이션

  • @PostConstruct: 해당 메서드는 객체가 생성된 후에 호출되며, 초기화 작업을 수행합니다.
  • @PreDestroy: 해당 메서드는 객체가 소멸되기 전에 호출되며, 정리 작업을 수행합니다.

Bootstrap 및 Channel 설정

  • Bootstrap 객체를 초기화하고, NioSocketChannel을 사용하여 채널을 설정합니다.
  • SO_KEEPALIVE 옵션을 사용하여 TCP keep-alive를 활성화합니다.
  • ChannelInitializer를 사용하여 채널 파이프라인을 초기화하고, 바이트 배열을 인코딩 및 디코딩하는 핸들러를 추가합니다.
  •  

댓글