0

I use Netty for a multithreaded TCP server and a single client persistent connection. The client sends many binary messages (10000 in my use case) and is supposed to receive an answer for each message. I added an OrderedMemoryAwareThreadPoolExecutor to the pipeline to handle the execution of DB calls on multiple threads.

If I run a DB call in the method messageReceived() (or simulate it with Thread.currentThread().sleep(50)) then all events are handled by a single thread.

    5 count of {main}
    1 count of {New
10000 count of {pool-3-thread-4}

For a simple implementation of messageReceived() the server creates many executor threads as expected.

How should I configure the ExecutionHandler to get multiple threads executors for the business logic, please?

Here is my code:

public class MyServer {

      public void run() {
            OrderedMemoryAwareThreadPoolExecutor eventExecutor = new OrderedMemoryAwareThreadPoolExecutor(16, 1048576L, 1048576L, 1000, TimeUnit.MILLISECONDS, Executors.defaultThreadFactory());  
            ExecutionHandler executionHandler = new ExecutionHandler(eventExecutor);        
            bootstrap.setPipelineFactory(new ServerChannelPipelineFactory(executionHandler));
      }
    }  



    public class ServerChannelPipelineFactory implements ChannelPipelineFactory {

      public ChannelPipeline getPipeline() throws Exception {

        pipeline.addLast("encoder", new MyProtocolEncoder());
        pipeline.addLast("decoder", new MyProtocolDecoder());
        pipeline.addLast("executor", executionHandler);
        pipeline.addLast("myHandler", new MyServerHandler(dataSource));

      }
    }

    public class MyServerHandler extends SimpleChannelHandler {

      public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e) throws DBException {


          // long running DB call simulation
          try {
            Thread.currentThread().sleep(50);
          } catch (InterruptedException ex) {

          }  

          // a simple message  
          final MyMessage answerMsg = new MyMessage();
          if (e.getChannel().isWritable()) {
            e.getChannel().write(answerMsg);
          }  
      }      
    }
Slava
  • 3
  • 2

1 Answers1

3

OrderedMemoryAwareThreadPoolExecutor guarantees that events from a single channel are processed in order. You can think of it as binding a channel to a specific thread in the pool and then processing all events on that thread - although it's a bit more complex than that, so don't depend on a channel always being processed by the same thread.

If you start up a second client you'll see it (most likely) being processed on another thread from the pool. If you really can process a single client's requests in parallel then you probably want MemoryAwareThreadPoolExecutor but be aware that this offers no guarantees on the order of channel events.

johnstlr
  • 1,431
  • 7
  • 6
  • I am getting an exception in the FrameDecoder now: the worker thread cannot cope with the number of messages coming from the client. Does it make sense if I add the executionHandler before the decoder in the pipeline? `ERROR {New I/O worker #1} (MyProtocolDecoder.java:161) - decode exception: java.lang.UnsupportedOperationException at org.jboss.netty.buffer.CompositeChannelBuffer.array(CompositeChannelBuffer.java:166) ... at org.jboss.netty.handler.codec.frame.FrameDecoder.callDecode(FrameDecoder.java:422)` – Slava Feb 10 '13 at 04:34
  • I've always tried to place the execution handler as far up the pipeline as possible, to allow the I/O thread to do as much work as it can. It's placement depends on where your blocking operations begin and also what's most performant for your app. Regarding the exception - CompositeChannelBuffer doesn't support the array() method. You may need DynamicChannelBuffer - http://static.netty.io/3.6/api/org/jboss/netty/buffer/DynamicChannelBuffer.html. Without seeing the decoder it's difficult to advise. However you'll probably want to ask for help on your decoder in a different quest. – johnstlr Feb 11 '13 at 09:13
  • Thank you. Yes, the error was in MyProtocolDecoder in the transformation of the ChannelBuffer into a Java object. It appears to work now. – Slava Feb 12 '13 at 21:21