2012-06-29 1 views
2

У меня есть проблема синхронизации в отношении запроса привязки и обработчика восходящего потока, который принимает событие channelBound. Мне нужно привязать объект к каналу, прежде чем обработчик сможет когда-либо получить событие channelBound из-за того, что обработчик должен использовать объект для обработки обратного вызова. Пример ниже.Как присоединить объект к каналу, прежде чем обработчики получат события с Netty?

Handler пример:

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()); 

Возможные Soultions

Я придумал несколько решений, чтобы обойти эту проблему, но они оба вида «запаха», поэтому я здесь спрашиваю, есть ли у кого чистый способ сделать это.

Решение 1: Спин или блок в обратном вызове channelBound до тех пор, пока вложение не будет равно нулю. Мне не нравится это решение, потому что он связывает работу с оператором ввода-вывода.

Решение 2: Сделайте MyClientHandler в двунаправленный обработчик и получите вложение с помощью ThreadLocal в обратном вызове bindRequested. Мне это не нравится, потому что он полагается на деталь реализации Netty, что запрашивающий поток используется для запуска события bindRequested.

Я нахожу решение 1 более терпимым, чем решение 2. Поэтому, если это то, что мне нужно сделать, я сделаю.

Есть ли простой способ получить ссылку на канал без запроса связывания или подключения в первую очередь?

ответ

2

Да, возможно, boundEvent может получить обработчик, прежде чем устанавливать вложение на канал.

Если вложение очень специфично для каждого канала вашего открытия, вы можете зарегистрировать канал будущего слушателя на привязке будущего и установить вложение на operationComplete() , настроив все без использования BootStraps. Ниже приведена модифицированная версия EchoClient Example, она отлично работает.

 // 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 
+0

Благодарим вас за ответ. Мне удалось решить проблему, используя ChannelFactory напрямую, как в вашем примере. – Dev

+1

Кроме того, ваш первый пример демонстрирует ту же проблему синхронизации. После вызова 'bind' обратный вызов' channelBound' мог быть вызван до того, как слушатель присоединяется к будущему привязки. Опять же, как в моем первом комментарии, хотя ваш второй пример именно то, что мне нужно. – Dev

+0

Согласен, извините за ошибку. Я удалил первый пример, чтобы не путать будущих зрителей :) –

2

Внесите свою реализацию ChannelPipelineFactory, примите параметр конструктора и укажите там приложение. Поместите обработчик перед всеми другими обработчиками и сделайте первый метод обработчика channelOpen(), который устанавливает вложение, а затем удалите первый обработчик из конвейера, потому что он больше не нужен.