SPRING&JAVA
[Netty] ChannelHandlerContext.close()와 Channel().close() 차이
ultra_dev
2024. 8. 25. 22:58
목차
- ctx.close()와 channel().close()의 차이
- 1.1 ctx.close()
- 1.2 channel().close()
- 주요 차이 요약
- ctx.close()로 발생할 수 있는 문제
- 3.1 이전 핸들러에서 이벤트 누락
- 3.2 중간 핸들러의 정리 작업 누락
- 흐름 비교
- 4.1 핸들러 호출 흐름 비교
- 4.2 결과 비교
- 4.3 흐름 비교 요약
- 4.4 흐름 비교 정리
- 최종 정리
- 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 결과 비교
- ctx.close() 호출 결과
BHandler에서 ctx.close()를 호출한 경우, 호출 흐름은 다음과 같음
BHandler: channelRead triggered
BHandler: channelInactive triggered
CHandler: channelInactive triggered
설명:
- BHandler 이후의 핸들러만 channelInactive() 이벤트를 수신
- AHandler는 이벤트를 수신하지 못함
- 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 흐름 비교 정리
- ctx.close():
- 특정 핸들러 이후로만 이벤트를 전달하고 싶을 때 적합
- 단, 이전 핸들러에서 리소스 정리가 필요한 경우 문제가 발생할 수 있음
- channel().close():
- 전체 핸들러에서 이벤트를 처리하도록 보장하므로, 정리 작업 누락 가능성을 방지
- 안전한 기본 동작을 제공하므로, 대부분의 경우 추천되는 방식
5. 최종 정리
5.1 ctx.close()
- 특정 핸들러 이후로만 이벤트를 전달하려는 경우에 적합.
- 이전 핸들러의 리소스 정리가 필요 없는 경우에 안전하게 사용할 수 있음.
- 단, 핸들러 설계에 따라 정리 작업이 필요한 핸들러가 이벤트를 받지 못할 수 있으므로 주의해야 함.
5.2 channel().close()
- 파이프라인 전체에 이벤트를 전달하므로, 모든 핸들러에서 정리 작업을 수행할 수 있음.
- 기본적으로 모든 핸들러가 정리 작업을 수행해야 하는 경우에 사용.
5.3 언제 무엇을 사용할까?
- 파이프라인 전체에서 이벤트 처리가 필요한 경우:
- channel().close()를 사용하여 안전성을 보장.
- 특정 핸들러 이후에만 이벤트 처리가 필요한 경우:
- ctx.close()를 신중히 사용.
- 이전 핸들러에서 이벤트 누락으로 인해 문제가 발생하지 않는지 확인.