Reputation: 11880
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:
An event loop of type NioEventLoopGroup
(i.e. a pool of shared threads that should not be subjected to blocking operations).
A channel of type NioServerSocketChannel
(I believe this is required to correspond with #1 above).
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
Reputation: 8257
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