0

最終編集/結論

これはnettyとは関係のない問題でしたが、それでもデバッグは非常に困難でした。messageReceivedのワーカースレッドがブロックされることがあったため、しばらくするとプールで使用可能なスレッドがなくなりました。

元の問題

私の会社では、nettyを使用してGPS追跡デバイスからの接続をリッスンしています。トラッカーはGPRSを介して通信します。

netty3.2.4-finalの非常に奇妙な動作を経験しました。

しばらくすると(正確にはわかりませんが、1日近く)、トラッカーからメッセージが届きません。これは、SimpleCahnnelUpstreamHandlerの実装のmessageReceivedメソッドが呼び出されないことを意味します。ただし、tcpdumpを使用してすべてのパケットをキャプチャすると、すべてのメッセージが着信するのを確認できます。

これは既知の問題ですか、それはnettyの新しいバージョンですでに修正されていますか?

チャネルパイプラインは次のようになります。

...
final TcpListenerChannelHandler tcpChannelHandler;


@Inject
public TcpListenerPipeline(TcpListenerChannelHandler tcpChannelHandler) {
     this.tcpChannelHandler = tcpChannelHandler;
}

@Override
public ChannelPipeline getPipeline() throws Exception {
        ChannelPipeline p = Channels.pipeline();
        p.addLast("frameDecoder", new DelimiterBasedFrameDecoder(2048, Delimiters.lineDelimiter()));
        p.addLast("encoder", new ByteArrayWrapperEncoder());
        p.addLast("handler", tcpChannelHandler);
        return p;
}
...

次の方法でリスニングをインスタンス化します。

public void startListen() {
        ChannelFactory channelFactory = new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool(),20);
        bootstrap = new ServerBootstrap(channelFactory);
        bootstrap.setPipelineFactory(pipeline);
        bootstrap.setOption("child.tcpNoDelay", true);
        bootstrap.setOption("child.keepAlive", true);
        lazyLogger.getLogger().info("Binding Tcp listener to 0.0.0.0 on port '{}'", listenPort);
        serverChannel = bootstrap.bind(new InetSocketAddress("0.0.0.0", listenPort));
}

誰かが何が間違っているのか手がかりを持っていますか?それとも、たとえば1時間程度ごとに、すべてのチャネルを手動で切断する必要がありますか?

編集:

問題についてもう少し情報があります

メッセージが処理されない場合、正常なリモート接続でchannelConnectedが呼び出されないことも発生します。問題をリモートでデバッグしたところ、次のことがわかりました。

  • NioServerSocketPipelineSink.java行#246でregisterAcceptedChannel(acceptedSocket、currentThread); 起こる
  • ソフトウェアの実行は、さまざまなイベントを伴うDefaultChannelPipeline行#781まで続きますが、私のTcpListenerChannelHandlerがコンテキストに含まれることはありません。

最も奇妙なのは、nettyがチャネルが接続されていることに気付く場合と、接続されていない場合があることです。

EDIT2:

TcpListenerCahnnelHandlerは、SimpleChannelUpstreamHandlerの単純な実装です。

それからのハイライト:

public class TcpListenerChannelHandler extends SimpleChannelUpstreamHandler {
...
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
    super.channelConnected(ctx, e);
    _logger.info("{} device connected from: {}", deviceProtocol.getName(),  ctx.getChannel().getRemoteAddress());
    deviceConnectionRegistry.channelConnected(ctx.getChannel());
}

@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
    super.channelDisconnected(ctx, e);
    _logger.info("{} device from endpoint '{}' disconnected.", deviceProtocol.getName(), ctx.getChannel().getRemoteAddress());
    deviceConnectionRegistry.channelDisconnected(ctx.getChannel());
}

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent messageEvent) throws Exception  {
    super.messageReceived(ctx, messageEvent);

    ...
    NOTE: here we process the meassage, I do not think it can cause any problem
}


@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
        if(_logger.isWarnEnabled())
            _logger.warn(deviceProtocol.getName()+ " device"
                    +e.getChannel().getRemoteAddress()+" channel", e.getCause());

        if (!(e.getCause() instanceof ConnectException))
            e.getChannel().close();
}

その間に、私は3.3.1-finalにアップグレードしました。また、問題が再発する場合は、デバッグを続行する場所がわかります。

編集3:

3.3.1ファイナルにアップグレードしましたが、2日後に同じ問題が再発しました。

それが関連しているかどうかはわかりませんが、同じ物理インターフェイス上にさらに多くのIPアドレスがあります。1つのインターフェースだけで聞いてみるべきですか?より多くのethインターフェースに関する既知の問題はありますか?

ただし、tcpdumpはトラッカーのメッセージを認識しますが、nettyはカスタムハンドラーでmessageReceivedを呼び出しません。

編集4:

コードをさらにデバッグしました。この問題はNioWorker.javaで発生します。131行目(ブール値提供= registerTaskQueue.offer(registerTask);)は正常に実行されますが、タスクは処理されません。つまり、748行目のRegisterTask.run()が呼び出されることはありません。

4

1 に答える 1

1

すべてを監視するために LoggingHandler を追加しようとしましたか? 私はカスタムハンドラーを使用していました:

/**
 *
 * Adapted from the original LoggingHandler in Netty.
 */
public class LoggingHandler implements ChannelUpstreamHandler, ChannelDownstreamHandler {

    String name;
    boolean hexDump;

    public LoggingHandler(String name, boolean hexDump) {
        this.name = name;
        this.hexDump = hexDump;
    }

    /**
     * Logs the specified event to the {@link InternalLogger} returned by
     * {@link #getLogger()}. If hex dump has been enabled for this handler,
     * the hex dump of the {@link ChannelBuffer} in a {@link MessageEvent} will
     * be logged together.
     */
    public void log(ChannelEvent e) {

        String msg = name + " >> " + e.toString();

        // Append hex dump if necessary.
        if (hexDump && e instanceof MessageEvent) {
            MessageEvent me = (MessageEvent) e;
            if (me.getMessage() instanceof ChannelBuffer) {
                ChannelBuffer buf = (ChannelBuffer) me.getMessage();
                msg = msg + " - (HEXDUMP: " + ChannelBuffers.hexDump(buf) + ')';
            }
        }

        // Log the message (and exception if available.)
        if (e instanceof ExceptionEvent) {
            Logger.debug(this, msg, ((ExceptionEvent) e).getCause());
        } else {
            Logger.debug(this, msg);
        }

    }



    public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
            throws Exception {
        log(e);
        ctx.sendUpstream(e);
    }

    public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e)
            throws Exception {
        log(e);
        ctx.sendDownstream(e);
    }

クライアント側とサーバー側の両方に挿入されます。サーバー側では、子と親の両方に追加するために使用します。

ChannelFactory factory = new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool());
        ServerBootstrap bootstrap = new ServerBootstrap(factory);
        bootstrap.setOption("child.tcpNoDelay", true);
        bootstrap.setOption("child.keepAlive", true);

        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {

            public ChannelPipeline getPipeline() throws Exception {
                ChannelPipeline pipeline = Channels.pipeline();
                pipeline.addLast("LOGGER", new LoggingHandler("SERVER", true));
                pipeline.addLast("LAUNCHER", handler.new OnChannelConnectedPlugger());
                return pipeline;
            }
        });
        bootstrap.setParentHandler(new LoggingHandler("SERVER-PARENT", true));
于 2012-04-13T12:41:20.683 に答える