멍청한 질문일수도 있어서 죄송합니다만
제가 생각했을때 예상되는 원인인것같아 이렇게 질문 올립니다.
현재 네티를 이용해서 다수의 서버(현장장비)에
클라이언트로 1:N 접속을 하여
TCP 통신으로 데이터를 수신받는부분을 처리하고있습니다.
서버와 연결되어있는 상태에 따라서 망 상태를 불량으로 인지하고 재접속하기 위해서
매 5초마다 서버에 응답요청패킷을 보냅니다.
그리고 readIdletime 을 60 으로 설정하여 (all, write 는 0)
망 단절이 일어난 경우 채널을 통해 수신받는 데이터가 없는경우,
재접속하도록 구현하였습니다.
하지만 현장 장비에 대해서 응답받는 패킷이 없는지
decode메소드가 동작하지 않지만
(수신받을때마다 무조건적으로 출력하는 로그로 판별했습니다.)
IdlestateHandler 의 channelIdle 메소드가 동작하지 않아 연결을 종료하지 않았고,
연결을 종료하지않았으므로 재연결이 되지 않았습니다.
현장서버에서 netstat 명령어로 확인해봐도,
사무실에서 현장장비에 붙여서 확인해봐도 동일한 증상이 지속적으로 발견되서
방법을 구글링해도 확인하기 어려워 질문드립니다.
IdlestateHandler 구현부분은 다음과 같습니다.
@Override
protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception
{
String PisID = pisManager.getPISCtlrNmbrByIp(NettyHelper.getRemoteAddress(ctx.channel()));
MDC.put("PIS", PisID);
logger.info("PIS ID : {} | channelIdle | IdleStateEvent : {} " , PisID , evt.state() );
try {
if(evt.state() == IdleState.READER_IDLE)
{
//특정 개소에서 간혹 동작하지 않는 부분입니다...
logger.info("Terminate Reader idle channel | 읽기 대기시간 초과 | PIS ID : {} | #Remote[{}:{}:{}]",
PisID, NettyHelper.getRemoteAddress(ctx.channel()), NettyHelper.getRemotePort(ctx.channel()), ctx.toString());
pisManager.getPISTemplateById( PisID ).getRecentStatus().setCMNC_STTS_CD(PISConStatus.Communication_DisConnect);
ctx.channel().close();
// ctx.channel().disconnect();
// super.channelIdle(ctx, evt);
// ctx.close();
logger.info("Terminate Reader idle channel ctx.channel().close");
// super.channelIdle(ctx, evt);
}
else if (evt.state() == IdleState.ALL_IDLE )
{
logger.info("Terminate All idle channel | PIS ID : {} | #Remote[{}:{}:{}]",
PisID, NettyHelper.getRemoteAddress(ctx.channel()), NettyHelper.getRemotePort(ctx.channel()), ctx.toString());
pisManager.getPISTemplateById(PisID).getRecentStatus().setCMNC_STTS_CD(PISConStatus.Communication_DisConnect);
ctx.channel().close();
// ctx.channel().disconnect().channel().close();
logger.info("Terminate All idle channel ctx.channel().close");
// super.channelIdle(ctx, evt);
}
else if(evt.state() == IdleState.WRITER_IDLE)
{
logger.info("Terminate Writer idle channel | 쓰기 대기시간 초과 | PIS ID : {} | #Remote[{}:{}:{}]",
PisID, NettyHelper.getRemoteAddress(ctx.channel()), NettyHelper.getRemotePort(ctx.channel()), ctx.toString());
// super.channelIdle(ctx, evt);
ctx.channel().close();
// ctx.channel().disconnect();
logger.info("Terminate Writer idle channel ctx.channel().close");
// super.channelIdle(ctx, evt);
}
}
catch(Exception e)
{
logger.error( "channelIdle | 작동중 예외 발생 | Exception log : {} " , LogHelper.getPrintStackTrace(e) );
}
finally
{
MDC.remove("PIS");
super.channelIdle(ctx, evt);
}
}
응답 요청 패킷을 1분으로 설정하고 응답대기시간을 5초로 설정하면 작동하긴 하지만
정상적인 동작을 위해서 그렇게 설정하지 않았습니다...
사용하는 네티 버전은 4.1.23.Final 입니다.
재접속 부분은
클라이언트 객체 생성시
clientChannel = connectChannelFuture
.addListener(new ConnectFutureListener(this)).sync().channel().closeFuture()
.addListener(new CloseFutureListener(this) ).sync().channel();
위와 같이 리스너를 이용해서 구현했습니다.
sync() 사용시 연결 재접속시 block Exception 이라는게 발생하는걸 확인하고
종료시 channel 연결 종료 상태에 따라 재접속 하도록 하였습니다.
스택 오버 플로에서 sync() 를 사용하지 않는것을 권장하는 글을 보긴 했지만
재접속 시도할 대기시간이 짧을수록 서버에 대한 접속 시도가 무한정 늘어나는것을 확인해서 sync()를 사용하였습니다...
------------------------------------------------------------------------------------------------------------------------------------
접속 리스너 클래스는
if(channelFuture.isCancelled()) {
logger.warn("Future Canceled | Connection Future | Retry Connect - {} | {}",channelFuture.channel().toString(), LogHelper.getPrintStackTrace(channelFuture.cause()));
channelFuture.channel().eventLoop().schedule(nettyClient, nettyClient.getReconnectTime(), TimeUnit.SECONDS);
}
else if (!channelFuture.isSuccess()) {
logger.warn("Connect Fail | Connection Future | Retry Connect - {} | {}",channelFuture.channel().toString(), LogHelper.getPrintStackTrace(channelFuture.cause()));
channelFuture.channel().eventLoop().schedule(nettyClient, nettyClient.getReconnectTime(), TimeUnit.SECONDS);
}
else if(!channelFuture.channel().isActive()) {
logger.warn("Channel In-Active | Connection Future | Retry Connect - {} | {}",channelFuture.channel().toString(), LogHelper.getPrintStackTrace(channelFuture.cause()));
channelFuture.channel().eventLoop().schedule(nettyClient, nettyClient.getReconnectTime(), TimeUnit.SECONDS);
}
}
catch(NullPointerException e)
{
logger.error("접속시 예외처리. 접속 예외 | NullPointerException | Exception Log : {} " , LogHelper.getPrintStackTrace(e) );
// channelFuture.channel().eventLoop().schedule(nettyClient, nettyClient.getReconnectTime(), TimeUnit.SECONDS);
}
catch(Exception e)
{
logger.error("접속시도중 예외처리 | EXCEPTION | Exception Log : {} " , LogHelper.getPrintStackTrace(e) );
// channelFuture.channel().eventLoop().schedule(nettyClient, nettyClient.getReconnectTime(), TimeUnit.SECONDS);
}finally {
}
------------------------------------------------------------------------------------------------------------------------------------
연결 종료 리스너 클래스는는
try
{
StringChannelStatus(channelFuture);
if(channelFuture.isSuccess()) {
logger.warn("Close Future | Success | Retry Connect - Channel Close | {}", channelFuture.channel().toString());
channelFuture.channel().eventLoop().schedule(nettyClient, nettyClient.getReconnectTime(), TimeUnit.SECONDS);
}else {
logger.warn("Close Future | Fail | Retry Connect - Channel Close | {} | {}", channelFuture.channel().toString(),channelFuture.cause());
channelFuture.channel().close();
}
}
catch(NullPointerException e){
logger.error("연결 종료 후. 재접속 오류. | PIS : {} | NullPointerException | Error Log : {} " , pisID, LogHelper.getPrintStackTrace(e) );
}
catch (Exception e) {
logger.error("연결 종료 후. 재접속시 예외사항 발생 | PIS ID : {} | errorLog : {} " , pisID , LogHelper.getPrintStackTrace(e));
}
finally {
}
답변 요청드리기 위해서 필요하다고 생각하는 부분의 모든 소스를 최대한 첨부하였습니다.
답변주시면 정말 감사하겠습니다!