Steve Perkins
Steve Perkins

Reputation: 11880

How can you safely perform blocking operations in a Netty channel handler?

I'm building a small Netty-based application that performs I/O operations across a socket connections (i.e. telnet/ssh). I am starting up my socket server with Netty's ServerBootstrap class, giving it:

  1. An event loop of type NioEventLoopGroup (i.e. a pool of shared threads that should not be subjected to blocking operations).

  2. A channel of type NioServerSocketChannel (I believe this is required to correspond with #1 above).

  3. A very simple pipeline, with a channel handler that extends ChannelInboundHandlerAdapter.

My handler's channelRead(...) method is called whenever a command string is received from a client socket connection, and returns some response string depending on the command.

Everything is fine for the commands which involve no blocking operations. However, there are SOME commands for which I now need to read from or write to a database. Those JDBC calls are inherently going to be blocking... although I could use a CompletableFuture (or whatever) to handle them in a separate thread.

But even if I did "roll-my-own async" by performing blocking operations in separate threads, I'm not sure how I would reconnect the results from those spawned threads back to the Netty channel handler in the main thread.

I see that the ChannelHandlerContext class has methods like:

ChannelFuture writeAndFlush(Object msg, ChannelPromise promise);

... as alternatives to the ones I'm currently using:

ChannelFuture writeAndFlush(Object msg);

But I can't find any documentation or guidance (or even helpful Javadocs) explaining how one might use this ChannelPromise type in this use case. It's name suggests that it might be relevant, but it might not be. After all, the writeAndFlush method still takes the outgoing message as its first parameter... so what good would it do to stuff your blocking operation into a "promise" second parameter, if you need its result to be already on-hand for the first parameter?

What is the right track here? Is there some way to handle blocking operations in separate threads, so that Netty's NioEventLoopGroup does not block? Or is this simply not how Netty works, and you should use a different event loop implementation (i.e. one that spawns a separate thread for each client socket connection) if you need to support blocking?

Upvotes: 9

Views: 6948

Answers (1)

If an operation in Netty takes longer time to complete or is blocking it is advisable to perform that in a handler that uses a separate ExecutorGroup so that the main EventLoop thread is not blocked.

You can specify that during the pipeline creation.

Quoting an example that uses executor group for DB operation from ChannelPipeline javadoc

static final EventExecutorGroup group = new DefaultEventExecutorGroup(16);
 ...

 ChannelPipeline pipeline = ch.pipeline();

 pipeline.addLast("decoder", new MyProtocolDecoder());
 pipeline.addLast("encoder", new MyProtocolEncoder());

 // Tell the pipeline to run MyBusinessLogicHandler's event handler methods
 // in a different thread than an I/O thread so that the I/O thread is not blocked by
 // a time-consuming task.
 // If your business logic is fully asynchronous or finished very quickly, you don't
 // need to specify a group.
 pipeline.addLast(group, "handler", new MyBusinessLogicHandler());

Upvotes: 9

Related Questions