I'd been waiting for ChannelPool to appear in Netty, to update my Netty HTTP Client[1] with a connection pool (per-host/port collection of open channels, so keep-alive connections can be reused).So I finally spent a few hours working on that, but the design of ChannelPool does not make it easy. So I'm wondering if this use case just isn't the kind of thing it was meant for (it looks like it was intended for things like JDBC-style connection pools - a bunch of connections to one host, period).Specific issues I'm running into:- SimpleChannelPool calls the no-argument bootstrap.connect(). Easy enough to have a subclass that knows the host+port and have one instance per host+port. However, FixedChannelPool is a final subclass of SimpleChannelPool, so the only way to deal with that is to copy the source code of FixedChannelPool. Better would be to unfinal FixedChannelPool and final most of its methods; better still, design-wise, would be to factor the code that establishes the connection into a separate class.
- SimpleChannelPool calls bootstrap.handler(), clobbering whatever handler was previously set up on it (i.e. one that does something useful!) - replacing it with one that simply calls ChannelPoolHandler on various events (I'm not sure what ChannelPoolHandler is good for - logging?). This is part of a larger issue that SimpleChannelPool assumes it has the Bootstrap to itself, and that is not necessarily true.
- ChannelPool.release() is clunky - a cleaner pattern would be for a channel pool to wrap the real Channel object in one that delegates to it, and simply uses calls to close() to release the channel back to the pool. That way you don't have to go find every call that closes a channel in your codebase, give it a reference to the ChannelPool and replace calls to close with pool.release(channel) - which means that a whole lot of code has to be ChannelPool aware that probably shouldn't.
- Why return Future<Channel> from ChannelPool.acquire(), instead of ChannelFuture (which is a Future<Void> and therefore cannot be returned by it)? If you're using any of the connect() methods on Bootstrap, you're *going* to get a ChannelFuture, and this just forces everybody to write a bunch of boilerplate to create a promise, listen on the ChannelFuture and update the promise on completion.
At this point, I'm considering just writing connection pooling from scratch without this stuff, but if I'm using it wrong and it is intended for this sort of use case, maybe there are some changes that could be made to make the API a little more friendly to it.
- ChannelPool.release() is clunky - a cleaner pattern would be for a channel pool to wrap the real Channel object in one that delegates to it, and simply uses calls to close() to release the channel back to the pool. That way you don't have to go find every call that closes a channel in your codebase, give it a reference to the ChannelPool and replace calls to close with pool.release(channel) - which means that a whole lot of code has to be ChannelPool aware that probably shouldn't.Norman and I discussed about this while designing ChannelPool and we decided not to override the close() operation. It's mainly because we don't really know what handles the pipeline contains and it is likely to introduce some conflicts.
--
You received this message because you are subscribed to a topic in the Google Groups "Netty discussions" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/netty/2v_QzVyMYlE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to netty+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/netty/1433906078.358733.291440753.428AF6A6%40webmail.messagingengine.com.
Hi Tim,Thank you very much for your feed back first of all.On Wed, Jun 10, 2015, at 10:57 AM, Tim Boudreau wrote:I'd been waiting for ChannelPool to appear in Netty, to update my Netty HTTP Client[1] with a connection pool (per-host/port collection of open channels, so keep-alive connections can be reused).So I finally spent a few hours working on that, but the design of ChannelPool does not make it easy. So I'm wondering if this use case just isn't the kind of thing it was meant for (it looks like it was intended for things like JDBC-style connection pools - a bunch of connections to one host, period).Specific issues I'm running into:- SimpleChannelPool calls the no-argument bootstrap.connect(). Easy enough to have a subclass that knows the host+port and have one instance per host+port. However, FixedChannelPool is a final subclass of SimpleChannelPool, so the only way to deal with that is to copy the source code of FixedChannelPool. Better would be to unfinal FixedChannelPool and final most of its methods; better still, design-wise, would be to factor the code that establishes the connection into a separate class.Sounds like a good idea. We could probably introduce an interface that is used to establish a new connection and its default implementation that uses Bootstrap.
- SimpleChannelPool calls bootstrap.handler(), clobbering whatever handler was previously set up on it (i.e. one that does something useful!) - replacing it with one that simply calls ChannelPoolHandler on various events (I'm not sure what ChannelPoolHandler is good for - logging?). This is part of a larger issue that SimpleChannelPool assumes it has the Bootstrap to itself, and that is not necessarily true.Introducing a new type dedicated for the establishment of a new connection (your suggestion in the first bullet point) will probably solve this issue together.- ChannelPool.release() is clunky - a cleaner pattern would be for a channel pool to wrap the real Channel object in one that delegates to it, and simply uses calls to close() to release the channel back to the pool. That way you don't have to go find every call that closes a channel in your codebase, give it a reference to the ChannelPool and replace calls to close with pool.release(channel) - which means that a whole lot of code has to be ChannelPool aware that probably shouldn't.Norman and I discussed about this while designing ChannelPool and we decided not to override the close() operation. It's mainly because we don't really know what handles the pipeline contains and it is likely to introduce some conflicts.
- Why return Future<Channel> from ChannelPool.acquire(), instead of ChannelFuture (which is a Future<Void> and therefore cannot be returned by it)? If you're using any of the connect() methods on Bootstrap, you're *going* to get a ChannelFuture, and this just forces everybody to write a bunch of boilerplate to create a promise, listen on the ChannelFuture and update the promise on completion.Returning a ChannelFuture instead of Future<Channel> sounds like a good idea to me.
At this point, I'm considering just writing connection pooling from scratch without this stuff, but if I'm using it wrong and it is intended for this sort of use case, maybe there are some changes that could be made to make the API a little more friendly to it.Thanks again for your feed back. I think you have some valid points and we should address them.
--
You received this message because you are subscribed to the Google Groups "Netty discussions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to netty+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/netty/1433903226.342493.291411713.452BAAC4%40webmail.messagingengine.com.
--
You received this message because you are subscribed to the Google Groups "Netty discussions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to netty+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/netty/625f1483-d1bc-4e27-a3cb-6366853ed620%40googlegroups.com.
- SimpleChannelPool calls the no-argument bootstrap.connect(). Easy enough to have a subclass that knows the host+port and have one instance per host+port. However, FixedChannelPool is a final subclass of SimpleChannelPool, so the only way to deal with that is to copy the source code of FixedChannelPool. Better would be to unfinal FixedChannelPool and final most of its methods; better still, design-wise, would be to factor the code that establishes the connection into a separate class.Sounds like a good idea. We could probably introduce an interface that is used to establish a new connection and its default implementation that uses Bootstrap.So you would inject this into the constructor ?
Norman and I discussed about this while designing ChannelPool and we decided not to override the close() operation. It's mainly because we don't really know what handles the pipeline contains and it is likely to introduce some conflicts.Exactly.
- Why return Future<Channel> from ChannelPool.acquire(), instead of ChannelFuture (which is a Future<Void> and therefore cannot be returned by it)? If you're using any of the connect() methods on Bootstrap, you're *going* to get a ChannelFuture, and this just forces everybody to write a bunch of boilerplate to create a promise, listen on the ChannelFuture and update the promise on completion.Returning a ChannelFuture instead of Future<Channel> sounds like a good idea to me.I thought about this and decided against it. The reason for this is that ChannelFuture.channel() can not be called until the right Channel is acquired from the pool (which will be done async). For Bootstrap.connect() this is not an issue as we create a new Channel even if the connect fails, which is different from what we need t do here (which is return the Channel instance that was prior created).
--
You received this message because you are subscribed to the Google Groups "Netty discussions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to netty+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/netty/8534d60c-c15b-412d-b070-aae4017a09c0%40googlegroups.com.
I did look at your code but I think wrapping the Channel opens a can of worms. Basically we would need to do a lot of hacks to ensure the "original" Channel never escaped. This includes wrapping:* ChannelPipeline* Channel* ChannelHandlerContext* ChannelHandler* ChannelFuture* ChannelPromise....So I think this is not feasible.