TCP 연결이 설정된 이후 정상적인 상황이라면 FIN 메시지를 송수신 했을 때, 연결이 해제됩니다.
하지만 전원이 차단되거나 오류에 의해 오동작을 하면 하나의 단말만 연결이 유지되는 Half Open Connection이 발생할 수 있습니다. 이러한 소켓이 꽉차면 정상적인 Connection요청에 응답을 할 수 없는 상황이 발생하게 됩니다.
이런 현상을 막기 위해 Keep-alive라는 기능이 있습니다.
TCP Keepalive
Tcp Keep-alive는 한 번 맺은 세션을 요청이 끝나더라도 유지해주는 기능입니다.
- tcp_keepalive_time으로 설정한 keepalive timeout 시간이 지나면 서버에서 Keepalive 확인 패킷을 보냅니다.
- 이 패킷에 대한 응답(ACK 패킷)을 받으면 타이머는 원래 값으로 돌아가 다시 카운트를 진행합니다.
- 응답을 받지 못한 경우에는 tcp_keepalive_intvl에 정의된 시간만큼 경과한 후 요청을 다시 보내게 되고, tcp_keepalive_probes 에 정의된 횟수만큼 보냅니다.
- 이후로도 응답이 없을 경우 클라이언트는 연결이 끊어졌다고 인지하고 서버에 RST 패킷을 보낸 다음 자신의 소켓을 닫습니다.
(* RST 패킷 : 비정상적인 세션 연결 끊기에 해당. 이 패킷을 발송하는 측이 현재 접속하고 있는 곳과 즉시 연결을 끊고자 할 때 사용)
Keepalive의 장점
- Keepalive 기능을 사용하면 불필요한 3 way handshake를 줄이고 TIME_WAIT 소켓도 줄일 수 있습니다.
- 연결이 끊어졌음에도 FIN 패킷을 받지 못해 정리되지 않고 남아있던 좀비 커넥션을 없애는 효과가 있습니다
Netty에서의 keep-alive 설정
Netty에서 사용하기 위해서는 Bootstrap에서 ChannelOption.SO_KEEPALIVE option으로 Keep-alive 기능을 활성화 하고, Keep-Alive 메세지를 보내는 기능을 위해 IdleStateHandler를 설정해야합니다.
참고로 서버와 클라이언트 양쪽에서 Keep-alive 설정을 하는 것이 바람직하고, 더 안정적입니다. 서로 유휴 상태를 감지할 수 있기 때문이죠
- 서버 Bootstrap에서 option 설정 (클라이언트는 .option으로 설정)
.childOption(ChannelOption.*SO_KEEPALIVE*,true) // Keep-alive 를 활성화
- ChannelPipeline에 IdleStateHandler 추가
p.addLast(new IdleStateHandler(60,30,0)) // 초단위로 설정
- Handler에서 Idle 상태가 발생했을 때 처리할 로직 작성
/**
* keep-alive 관련 설정
* @param ctx
* @param obj
* @throws Exception
*/
@Override
public void userEventTriggered( ChannelHandlerContext ctx, Object obj ) throws Exception {
if( obj instanceof IdleStateEvent ){
IdleStateEvent event = ( IdleStateEvent ) obj;
InetSocketAddress inetSocketAddress = ( InetSocketAddress ) ctx.channel().remoteAddress();
if( event.state() == IdleState.READER_IDLE ){
// 읽기 유휴 상태인 경우
log.info(TcpMessageCode.READER_IDLE.getMessage(), inetSocketAddress.getAddress(), inetSocketAddress.getPort() );
ctx.close(); // 채널을 닫는다
}else if( event.state() == IdleState.WRITER_IDLE ){
// 쓰기 유휴 상태인 경우
log.info(TcpMessageCode.WRITER_IDLE.getMessage(), inetSocketAddress.getAddress(), inetSocketAddress.getPort() );
ctx.close(); // 채널을 닫는다
}
}
}
'Java > Netty' 카테고리의 다른 글
ChannelGroup과 GlobalEventExecutor (0) | 2024.07.01 |
---|---|
Netty의 이벤트 루프 이해하기 (0) | 2024.06.20 |