Programmatically create multiple connections for T

2019-08-20 15:08发布

问题:

Continuing the conversation from this question:

Two-part question here:

Can a TcpNetClientConnectionFactory have multiple connections to its upstream server, if the host and port are the same?

If so, how can I programmatically build a new connection for that connection factory? I see the buildNewConnection method, but it is protected.

The first connection is automatically built as soon as the first Message passes through the factory. What we need to do is notice when following Messages have a different ip_connectionId, stand up a new connection, and route those Messages to that new connection. Obviously, Messages with the original ip_connectionId would still be routed to the original connection.

Not sure whether it would be better to create multiple connections off of one connection factory, or create a whole new connection factory, sending message handler, and receiving channel adapter for each new connection.

回答1:

If the inbound connection factory is a TcpNetServerConnectionFactory, you can simply use a ThreadAffinityClientConnectionFactory because each inbound connection gets its own thread.

You would call getConnection(). This will bind the connection to the thread (and you can obtain the connection id from it), but you don't really need to map the header in this direction because of the thread affinity, you would only have to map on the return path.

Bear in mind, though, if the ThreadAffinityClientConnectionFactory detects that a connection has been closed, it will create a new one. So, you might want to call getConnection() in your mapper on each call. However, there would still be a race condition, so you might also need to listen for TcpConnectionCloseEvents and TcpConnectionOpenEvents.

If you use NIO on the inbound, or otherwise hand off the work to other threads via an executor, this won't work.

In that case you would need your own wrapping connection factory - you could use the ThreadAffinityClientConnectionFactory as a model, but instead of storing the connections in a ThreadLocal, you'd store them in a map. But you'd still need a ThreadLocal (set upstream on each call) to tell the factory which connection to hand out when the adapter asks for one.

There's a trick you need to be aware of, however.

There is a property singleUse on the connection factory. This serves 2 purposes;

  • first, it tells the factory to create a new connection each time getConnection() is called instead of a single, shared, connection
  • second, it tells the inbound adapter to close the connection after the reply is received

So the trick is you need singleUse=true on the real factory (so it gives you a new connection each time getConnection() is called), but singleUse=false on the wrapping factory so the adapters don't close the connection.

I suggest you look at the ThreadAffinityClientConnectionFactory and CachingClientConnectionFactory connection factory to see how they work.

We should probably consider splitting this into two booleans; we could probably also make some improvements to avoid the need for a thread local by adding something like getConnection(String connectionId) to the client factory contract and have the factory look up the connection internally; but that will require work in the adapters.

I'll capture an issue for this and see if we can get something in 5.2.

Rather a long answer, but I hope it makes sense.