在处理程序通过Netty接收事件之前如何将对象附加到通道?

时间:2012-06-29 21:25:24

标签: java netty

我遇到有关绑定请求和接收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());

可能的结果

我已经提出了几个解决方案来解决这个问题,但它们都有点“嗅觉”,这就是为什么我在这里问是否有人干净利落的做法。

解决方案1:在channelBound回调中旋转或阻止,直到附件不为空。我不喜欢这个解决方案,因为它会占用一个I / O工作者。

解决方案2:将MyClientHandler置于双向处理程序中,并使用ThreadLocal下游回调中的bindRequested获取附件。我不喜欢这个,因为它依赖于Netty实现细节,请求线程用于触发bindRequested事件。

我发现解决方案1比解决方案2更容忍。所以,如果这是我需要做的,我会。

是否有一种简单的方法可以在不请求绑定或首先连接的情况下获取通道引用?

2 个答案:

答案 0 :(得分:2)

让您的ChannelPipelineFactory实现接受构造函数参数并在那里指定附件。将处理程序放在所有其他处理程序的前面,并使第一个处理程序的channelOpen()方法设置附件,然后从管道中删除第一个处理程序,因为它不再需要了。

答案 1 :(得分:2)

是的,在将通道设置为通道之前,boundEvent可能会获得处理程序。

如果附件非常特定于您打开的每个频道,那么您可以在绑定未来注册频道未来侦听器并在operationComplete()上设置附件 通过设置所有内容而不使用BootStraps。以下是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