netty 3.5.7 Channel.close()抛出异常导致closefuture未通知

时间:2013-03-07 02:53:42

标签: java netty

我正在挖掘我的netty程序中的一个错误:我在服务器和客户端之间使用了一个心跳处理程序,当客户端系统重新启动时,服务器端的心跳处理程序将知道超时,然后关闭通道,但有时候在Channel的CloseFuture注册的监听器永远不会被通知,这很奇怪。

在挖掘netty 3.5.7源代码之后,我发现通道的CloseFuture被通知的唯一方法是通过AbstractChannel.setClosed();可能在Channel关闭时不执行此方法,见下文:

NioServerSocketPipelineSink:

private static void close(NioServerSocketChannel channel, ChannelFuture future) {
    boolean bound = channel.isBound();
    try {
        if (channel.socket.isOpen()) {
            channel.socket.close();
            Selector selector = channel.selector;
            if (selector != null) {
                selector.wakeup();
            }
        }

        // Make sure the boss thread is not running so that that the future
        // is notified after a new connection cannot be accepted anymore.
        // See NETTY-256 for more information.
        channel.shutdownLock.lock();
        try {
            if (channel.setClosed()) {
                future.setSuccess();
                if (bound) {
                    fireChannelUnbound(channel);
                }
                fireChannelClosed(channel);
            } else {
                future.setSuccess();
            }
        } finally {
            channel.shutdownLock.unlock();
        }
    } catch (Throwable t) {
        future.setFailure(t);
        fireExceptionCaught(channel, t);
    }
}

在某些平台上,channel.socket.close()可能会抛出IOException,这意味着可能永远不会执行channel.setClosed(),因此可能不会通知在CloseFuture中注册的侦听器。

这是我的问题:你遇到过这个问题吗?分析是对的吗?

我发现它是我的心跳处理程序导致问题:永远不会超时,所以永远不要关闭频道,下面是在计时器中运行:

if ((now - lastReadTime > heartbeatTimeout)
                    && (now - lastWriteTime > heartbeatTimeout)) {
                getChannel().close();
                stopHeartbeatTimer();
            }

其中lastReadTime和lastWriteTime更新如下:

public void writeComplete(ChannelHandlerContext ctx, WriteCompletionEvent e)
        throws Exception {
    lastWriteTime = System.currentTimeMillis();
    super.writeComplete(ctx, e);
}

public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
        throws Exception {
    lastReadTime = System.currentTimeMillis();
    super.messageReceived(ctx, e);
}

远程客户端是Windows xp,当前服务器是Linux,都是jdk1.6。 我认为在远程客户端系统重启后仍然在内部调用writeComplete,虽然没有调用messageReceived,但在此期间没有抛出IOExceptoin。

我将重新设计心跳处理程序,在心跳包中附加时间戳和HEART_BEAT标志,当对等方收到数据包时,当当前端收到此确认包时,发送回具有相同时间戳和ACK_HEART_BEAT标志的数据包,使用此时间戳更新lastWriteTime。

0 个答案:

没有答案