私は netty プログラムのバグを掘り下げています: 私はサーバーとクライアントの間でハートビート ハンドラーを使用しました。 Channel の CloseFuture は通知されません。これは奇妙です。
netty 3.5.7 ソース コードを掘り下げた後、チャネルの CloseFuture を通知する唯一の方法は、AbstractChannel.setClosed() を使用することであることがわかりました。チャネルが閉じているときにこのメソッドが実行されない可能性があります。以下を参照してください。
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 フラグを付けてパケットを送り返し、現在の側がこの ack パケットを受信したときに、これを使用します。 lastWriteTime を更新するタイムスタンプ。