SPRING&JAVA

[Netty] ChannelHandlerContext.close()와 Channel().close() 차이

ultra_dev 2024. 8. 25. 22:58

목차

  1. ctx.close()와 channel().close()의 차이
    • 1.1 ctx.close()
    • 1.2 channel().close()
  2. 주요 차이 요약
  3. ctx.close()로 발생할 수 있는 문제
    • 3.1 이전 핸들러에서 이벤트 누락
    • 3.2 중간 핸들러의 정리 작업 누락
  4. 흐름 비교
    • 4.1 핸들러 호출 흐름 비교
    • 4.2 결과 비교
    • 4.3 흐름 비교 요약
    • 4.4 흐름 비교 정리
  5. 최종 정리
    • 5.1 ctx.close()
    • 5.2 channel().close()
    • 5.3 언제 무엇을 사용할까?


참고) 편의상 ChannelHandlerContext → ctx, Channel -> channel로 줄임

 

1. ctx.close()와 channel().close()의 차이

1.1 ctx.close()

  • 현재 핸들러 이후의 파이프라인에만 이벤트를 전달
  • 현재 핸들러 이후의 핸들러들은 channelInactive() 이벤트를 받을 수 있지만, 이전 핸들러들은 이벤트를 받지 못함
  • 특정 핸들러 이후로만 이벤트를 전파하고 싶을 때 유용

1.2 channel().close()

  • 파이프라인 전체의 모든 핸들러에 이벤트를 전달
  • 모든 핸들러가 channelInactive() 이벤트를 받을 수 있으므로, 정리 작업이나 리소스 해제가 필요한 핸들러에서도 안전하게 작동

2. 주요 차이 요약

 

특징 ctx.close()  channel().close()
이벤트 전달 범위 현재 핸들러 이후의 핸들러들만 이벤트 전달 파이프라인의 모든 핸들러에 이벤트 전달
주요 사용 사례 특정 핸들러 이후에만 채널 닫기를 처리하고 싶을 때 전체 핸들러에서 이벤트를 처리해야 할 때
문제 발생 가능성 이전 핸들러에서 이벤트 누락 가능성 이벤트 누락 문제 없음
리소스 자동 정리 여부 - 기본 리소스(Netty 내부 리소스)는 자동으로 정리됨
- 외부 리소스 : channelInactive()에 리소스 정리 로직을 설정해도 이전 핸들러에서는 정리 작업 미실행 
- 기본 리소스(Netty 내부 리소스)는 자동으로 정리됨
- 외부 리소스 : channelInactive()에 리소스 정리 로직을 설정하면 모든 핸들러에서 이벤트 호출 가능 


 

3. ctx.close()로 발생할 수 있는 문제

3.1 이전 핸들러에서 이벤트 누락

  • 설명:
    • ctx.close()는 현재 핸들러 이후의 핸들러에만 channelInactive() 이벤트를 전달
    • 현재 핸들러 이전의 핸들러들은 channelInactive() 이벤트를 받지 못하므로, 리소스 정리 로직이 실행되지 않을 수 있음
  • 문제 발생 시나리오:
    • 이전 핸들러가 네트워크 리소스나 외부 연결(DB 등)을 관리하고 있는 경우, 이벤트 누락으로 인해 정리 작업이 실행되지 않을 수 있음
public class AHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        System.out.println("AHandler: Cleaning up resources");
        // 리소스 정리 작업
    }
}

public class BHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ctx.close(); // AHandler는 channelInactive() 이벤트를 받지 못함
    }
}
  • 결과:
    • A 핸들러에서 리소스 정리 작업이 실행되지 않음

4. 흐름 비교: 핸들러 호출 흐름 시뮬레이션

4.1 핸들러 호출 흐름 비교

ctx.close()와 channel().close()의 실제 호출 흐름을 파악하기 위해 핸들러를 여러 개 추가하고, 이벤트 전파 방식을 코드로 확인


예제 코드: 핸들러 구현

public class AHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        System.out.println("AHandler: channelInactive triggered");
    }
}

public class BHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        System.out.println("BHandler: channelInactive triggered");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("BHandler: channelRead triggered");
        ctx.close(); // ctx.close() 호출
    }
}

public class CHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        System.out.println("CHandler: channelInactive triggered");
    }
}


파이프라인 구성

pipeline.addLast("AHandler", new AHandler());
pipeline.addLast("BHandler", new BHandler());
pipeline.addLast("CHandler", new CHandler());


4.2 결과 비교

  1. ctx.close() 호출 결과

BHandler에서 ctx.close()를 호출한 경우, 호출 흐름은 다음과 같음

BHandler: channelRead triggered
BHandler: channelInactive triggered
CHandler: channelInactive triggered

설명:

  • BHandler 이후의 핸들러만 channelInactive() 이벤트를 수신
  • AHandler는 이벤트를 수신하지 못함

  1. channel().close() 호출 결과

BHandler에서 channel().close()를 호출한 경우, 호출 흐름은 다음과 같음

BHandler: channelRead triggered
AHandler: channelInactive triggered
BHandler: channelInactive triggered
CHandler: channelInactive triggered

설명:

  • 파이프라인의 모든 핸들러가 channelInactive() 이벤트를 수신
  • 모든 핸들러에서 정리 작업을 수행할 수 있음

4.3 흐름 비교 요약

특징 ctx.close()  channel().close()
이벤트 전파 범위 현재 핸들러 이후의 핸들러로만 전파 파이프라인 전체에 전파
AHandler 이벤트 수신 여부 이벤트를 받지 않음 이벤트를 받음
CHandler 이벤트 수신 여부 이벤트를 받음 이벤트를 받음
리소스 정리 작업 누락 가능성 이전 핸들러에 정리 작업이 누락될 가능성 있음 정리 작업 누락 가능성이 없음

4.4 흐름 비교 정리

  1. ctx.close():
    • 특정 핸들러 이후로만 이벤트를 전달하고 싶을 때 적합
    • 단, 이전 핸들러에서 리소스 정리가 필요한 경우 문제가 발생할 수 있음
  2. channel().close():
    • 전체 핸들러에서 이벤트를 처리하도록 보장하므로, 정리 작업 누락 가능성을 방지
    • 안전한 기본 동작을 제공하므로, 대부분의 경우 추천되는 방식

5. 최종 정리

5.1 ctx.close()

  • 특정 핸들러 이후로만 이벤트를 전달하려는 경우에 적합.
  • 이전 핸들러의 리소스 정리가 필요 없는 경우에 안전하게 사용할 수 있음.
  • 단, 핸들러 설계에 따라 정리 작업이 필요한 핸들러가 이벤트를 받지 못할 수 있으므로 주의해야 함.

5.2 channel().close()

  • 파이프라인 전체에 이벤트를 전달하므로, 모든 핸들러에서 정리 작업을 수행할 수 있음.
  • 기본적으로 모든 핸들러가 정리 작업을 수행해야 하는 경우에 사용.

5.3 언제 무엇을 사용할까?

  1. 파이프라인 전체에서 이벤트 처리가 필요한 경우:
    • channel().close()를 사용하여 안전성을 보장.
  2. 특정 핸들러 이후에만 이벤트 처리가 필요한 경우:
    • ctx.close()를 신중히 사용.
    • 이전 핸들러에서 이벤트 누락으로 인해 문제가 발생하지 않는지 확인.