2

channelBoundバインド リクエストと、イベントを受け取るアップストリーム ハンドラに関する同期の問題があります。channelBoundハンドラーがオブジェクトを使用してコールバックを処理する必要があるため、ハンドラーがイベントを受け取る前にオブジェクトをチャネルにアタッチする必要があります。以下の例。

ハンドラーの例:

public class MyClientHandler extends SimpleChannelUpstreamHandler {

    @Override
    public void channelBound(ChannelHandlerContext ctx, ChannelStateEvent e) {

        /* Problem: This can occur while the channel attachment is still null. */
        MyStatefulObject obj = e.getChannel().getAttachment();

        /* Do important things with attachment. */ 
    }

}

主な例:

ClientBootstrap bootstrap = ... //Assume this has been configured correctly.

ChannelFuture f = bootstrap.bind(new InetSocketAddress("192.168.0.15", 0));

/* It is possible the boundEvent has already been fired upstream
 *  by the IO thread when I get here. 
 */
f.getChannel().setAttachment(new MyStatefulObject());

可能なソウルション

私はこれを回避するためのいくつかの解決策を考え出しましたが、どちらも一種の「臭い」です。

channelBound解決策 1:添付ファイルが null でなくなるまで、コールバックでスピンまたはブロックします。I/O ワーカーを拘束するため、このソリューションは好きではありません。

解決策 2:MyClientHandler双方向ハンドラーを作成し、ダウンストリーム コールバックで を使用して添付ファイルを取得しThreadLocalますbindRequestedbindRequested要求スレッドがイベントを起動するために使用される Netty 実装の詳細に依存しているため、私はこれが好きではありません。

解決策 1 は解決策 2 よりも許容範囲が広いことがわかりました。

最初にバインドまたは接続を要求せずにチャネル参照を取得する簡単な方法はありますか?

4

2 に答える 2

2

はい、チャネルにアタッチメントを設定する前に、boundEvent がハンドラーを取得できる可能性があります。

アタッチメントが、開いているすべてのチャネルに非常に固有のものである場合は、バインド フューチャーにチャネル フューチャー リスナーを登録し、BootStraps を使用せずにすべてを設定することで、operationComplete() にアタッチメントを設定できます。以下は、EchoClient の例の修正版です。正常に動作します。

       // Configure the client.
    final NioClientSocketChannelFactory clientSocketChannelFactory = new NioClientSocketChannelFactory(
            Executors.newCachedThreadPool());


    // Set up the pipeline factory.
    final ChannelPipelineFactory channelPipelineFactory = new ChannelPipelineFactory() {
        public ChannelPipeline getPipeline() throws Exception {
            return Channels.pipeline(
                    new MyClientHandler());
        }
    };

    ChannelPipeline pipeline = channelPipelineFactory.getPipeline();
    final Channel channel = clientSocketChannelFactory.newChannel(pipeline);

    channel.getConfig().setPipelineFactory(channelPipelineFactory);
    channel.getConfig().setOption("tcpNoDelay", true);
    channel.getConfig().setOption("receiveBufferSize", 1048576);
    channel.getConfig().setOption("sendBufferSize", 1048576);

    ChannelFuture boundFuture = Channels.future(channel);

    boundFuture.addListener(new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            if (future.isSuccess()) {
                future.getChannel().setAttachment(new Object());// set the channel attachment
            }
        }
    });


    channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(channel, boundFuture, ChannelState.BOUND, new InetSocketAddress(host, 0)));

    ChannelFuture connectFuture = Channels.future(channel); 
    channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(channel, connectFuture, ChannelState.CONNECTED, new InetSocketAddress(host, port)));

    channel.getCloseFuture().awaitUninterruptibly();

    clientSocketChannelFactory.releaseExternalResources();// do not forget to do this
于 2012-06-30T10:00:48.650 に答える
2

実装ChannelPipelineFactoryでコンストラクタ パラメータを受け入れ、そこでアタッチメントを指定します。ハンドラーを他のすべてのハンドラーの前に配置し、最初のハンドラーのchannelOpen()メソッドでアタッチメントを設定してから、パイプラインから最初のハンドラーを削除します。

于 2012-06-30T09:13:59.057 に答える