netty 타임아웃 설정에 관하여 문의드립니다.

5,953 views
Skip to first unread message

이홍일

unread,
Jun 23, 2016, 3:45:50 AM6/23/16
to Netty Korean User Group
안녕하세요. netty를 회사 프로젝트에서 사용하고 있습니다.

질문은 타임 아웃 설정에 관한 것입니다.

1. connection 타임아웃
제목 그대로 connection을 시도하다 일정시간이 지나도 접속이 되지 않으면 예외를 날려주거나 하는 것을 구현하려고 하는데요

ChannelFuture f = bootstrap.connect(host, port).sync();
ch = f.channel();
SocketChannelConfig cfg = (SocketChannelConfig)ch.config();
cfg.setConnectTimeoutMillis(timeOut);

이렇게 구현하면 되나요? 
connect를 하기 전에 설정을 해야할 것 같은데 위 코드와 같이 하면 connect후에 설정하게 되는 것 같아서요.


2. 패킷을 보낸 후 일정 시간내 답이 오지 않으면 소켓을 닫는 타임아웃

pipeline.addLast(new ReadTimeoutHandler(10));

이렇게 하면 connect하고 패킷을 보내지 않아도 10초 동안 read하는게 없으면 exception이 뜨더군요.
제가 원하는 건 write하고 10초 내에 답이 없으면 소켓을 닫는 것인데요
타이머 같은 것을 사용하여 구현해야 하나요?

읽어주셔서 감사합니다.

Hwan Jo Kim

unread,
Jun 24, 2016, 12:55:29 AM6/24/16
to Netty Korean User Group
1. Bootstrap 설정에서 option을 사용해서 설정하면 됩니다.

Bootstrap b = new Bootstrap();
b.group(group)
  .channel(NioSocketChannel.class)
  .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3 * 1000)
  .handler(new ChannelInitializer<SocketChannel>() {
  

2번의 경우는 IdleStateHandler를 분석하시면 도움이 될거 같습니다.
ReaderIdleTimeoutTask를 write가 완료되면 실행되도록 수정하면 원하시는 기능이 가능할 듯 합니다.
테스트한 파일을 첨부하오니 그대로 사용하지 마시고 꼭 수정해서 사용하세요

부디 도움이 되길 바랍니다




2016년 6월 23일 목요일 오후 4시 45분 50초 UTC+9, 이홍일 님의 말:
ReadIdleAfterWritingStateHandler.java

이홍일

unread,
Jun 24, 2016, 2:45:21 AM6/24/16
to Netty Korean User Group
정말 큰 도움이 되었습니다. 

netty에서 함수를 제공할 거라고 생각하고 직접 구현할 생각은 하지 않았네요.
개발자로서 반성하게 됩니다. 

감사합니다. 



2016년 6월 24일 금요일 오후 1시 55분 29초 UTC+9, Hwan Jo Kim 님의 말:

이홍일

unread,
Jun 29, 2016, 3:26:34 AM6/29/16
to Netty Korean User Group
Hwan Jo Kim 님의 코드와 IdleStateHandler를 분석해서 다음과 같이 구현했습니다. 


public class ReadIdleAfterWritingStateHandler extends ChannelDuplexHandler {
private static final long MIN_TIMEOUT_NANOS = TimeUnit.MILLISECONDS.toNanos(1);
    
    // writing한 후 reading이 없는 제한시간
private final long readTimeout;
private volatile ScheduledFuture<?> timeout;


    private volatile long lastWriteTime;
    private volatile boolean readed;
    
    public ReadIdleAfterWritingStateHandler(long readerIdleTimeAfterWriting) {
        if (readerIdleTimeAfterWriting <= 0) 
        throw new InvalidParameterException("time must > 0");
        
        readTimeout = Math.max(TimeUnit.SECONDS.toNanos(readerIdleTimeAfterWriting), MIN_TIMEOUT_NANOS);
}
    public long getReaderIdleTimeInMillis() {
        return TimeUnit.NANOSECONDS.toMillis(readTimeout);
    }
    
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        destroy();
        super.handlerRemoved(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        destroy();
        super.channelInactive(ctx);
    }
    
    @Override
    public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        // Allow writing with void promise if handler is only configured for read timeout events.
        if (readTimeout > 0) {
            ChannelPromise unvoid = promise.unvoid();
            ctx.write(msg, unvoid).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
lastWriteTime = System.nanoTime();
           initialize(ctx);
}
});
        } else {
            ctx.write(msg, promise);
        }
    }
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
    destroy();
    readed = true;
    
    ctx.fireChannelRead(msg);
    }
    
    private void initialize(ChannelHandlerContext ctx) {
    readed = false;
    
    if(timeout!= null && !timeout.isDone())
    timeout.cancel(false);
   
        timeout = ctx.executor().schedule(new ReaderIdleTimeoutTask(ctx), readTimeout, TimeUnit.NANOSECONDS);
    }

    synchronized private void destroy() {
        if (timeout != null) {
            timeout.cancel(false);
            timeout = null;
        }
    }
    
    /**
     * Is called when an {@link IdleStateEvent} should be fired. This implementation calls
     * {@link ChannelHandlerContext#fireUserEventTriggered(Object)}.
     */
    protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
    ctx.fireExceptionCaught(ReadTimeoutException.INSTANCE);
        ctx.close();
    }
    
    
    private final class ReaderIdleTimeoutTask implements Runnable {

        private final ChannelHandlerContext ctx;

        ReaderIdleTimeoutTask(ChannelHandlerContext ctx) {
            this.ctx = ctx;
        }

        public void run() {
            if (!ctx.channel().isOpen()) 
                return;

            long nextDelay = readTimeout;
            if (readed) {
            destroy();
            return;
            }
            else
                nextDelay -= System.nanoTime() - lastWriteTime;

            if (nextDelay <= 0) {
                // Reader is idle - set a new timeout and notify the callback.
                //readerIdleTimeoutAfterWriting = ctx.executor().schedule(this, readerIdleTimeNanosAfterWriting, TimeUnit.NANOSECONDS);
           
            destroy();
                
                try {
                    channelIdle(ctx, IdleStateEvent.READER_IDLE_STATE_EVENT);
                } 
                catch (Throwable t) {
                    ctx.fireExceptionCaught(t);
                }
            } 
            else {
                // Read occurred before the timeout - set a new timeout with shorter delay.
            timeout = ctx.executor().schedule(this, nextDelay, TimeUnit.NANOSECONDS);
            }
        }
    }
}

고민은 ReaderIdleTimeoutTask의 run()에서 
            
           if (readed) {
            destroy();
            return;
            }
            else
                nextDelay -= System.nanoTime() - lastWriteTime;

이 부분인데요 readed가 false여서 return을 하지 않고 계속 진행하는 도중에 
패킷이 들어와서 public void channelRead(ChannelHandlerContext ctx, Object msg)가 호출되는 경우입니다.

IdleStateHandler를 분석해봤는데 제 능력에서는 위와 같은 경우에 어떻게 되는 건지 잘 모르겠더라구요.
조언을 해주시면 정말 큰 도움이 될 것 같습니다.

감사합니다.


2016년 6월 24일 금요일 오후 3시 45분 21초 UTC+9, 이홍일 님의 말:

Hwan Jo Kim

unread,
Jun 29, 2016, 8:59:57 PM6/29/16
to Netty Korean User Group
제가 이해한 질문의 내용은 write후에 정해진 시간내에 read가 발생하지 않아서 처리하는 도중에 read 이벤트가 발생하면 어떻게 해야 하는가 입니다.
맞나요? ^^

channelRead가 발생했다면 시간내에 발생한 이벤트인지를 판별하는게 중요할 듯 합니다.

먼저 해당 ScheduledFuture를 cancel처리하고 현재시간과 마지막으로 writing한 시간의 차이가 
10초 이내라면 다음 handler를 호출하면 될 것이고
10초 이상이라면 channelIdle()을 호출하면 될 거 같습니다

부디 도움이 되길 바랍니다.


2016년 6월 23일 목요일 오후 4시 45분 50초 UTC+9, 이홍일 님의 말:
안녕하세요. netty를 회사 프로젝트에서 사용하고 있습니다.
Reply all
Reply to author
Forward
0 new messages