|
adding some more thoughts -
the problem is in AbstractConnector.getDispatcher(UMOImmutableEndpoint) and similarly getReceiver(UMOComponent, UMOEndpoint). A simple "fix" would be to have a KeyedObjectPool that manages things by endpoint key and only hands out a new dispatcher/receiver when the old one has been returned. I do not know (suspect not) if this is a good idea since it means that only one request or dispatch for a given connector's endpoint can run at the same time. One other alternative approach would be to keep the currently used ConcurrentMap + synchronized retrieval on endpoint (which is an appropriate design, scales nicely and has minimal overhead) and simply put a pool into the map, so that each endpoint key maps to several receivers/dispatchers "standing in line". The existing Dispatcher/Receiver factory could be wrapped as PoolFactory which would take care of lifecycle control like handout/return/creation/cleanup. I gave some more thought to this problem and its wider implications. The one thing that I'm not entirely clear about is whether multiple receivers/dispatchers can or must not exist for the same endpoint? My hunch is that this depends on the concrete type of connector. If only one "connection" per endpoint is possible (like a single socket to a specific port) then the answer would be no. This might be just a specific case that either the connector or the receiver/dispatcher factories can handle (generally allowing for multiple handlers (trick question: how many?
Status update on this since the comments are mostly outdated so far. The management of dispatchers has been put under the control of a KeyedObjectPool that maps each endpoint to a pool of dispatchers. These are lifecycle-managed by a UMOMessageDispatcherFactory subclass that creates/activates/validates/passivates/destroys them as approrpiate. All existing transports have been adapted to this new scheme and it greatly simplifies the control flow and state management inside a singel dispatcher, which is now guaranteed to be only used by a single thread at a time. More documentation will be available on the Wiki.
Lajos asked about the process of inspecting existing transports for the new dispatcher handling so here's a small writeup. It is obviously incomplete since the Wiki docs are not yet ready but it should provide simple guidelines to follow:
Basically the less external state the do* methods require, the better. Since we now have multiple dispatchers, they actually can simply use instance variables more freely; however a dispatcher still needs to make sure that they are properly managed between do* invocations. A dispatcher can dispose() itself (e.g. after an unrecoverable exception) which by default will make AbstractMessageDispatcher.validate() return false. Any other additional condition can be used to return true or false from validate, that depends on the dispatcher subclass. This might be useful for when a dispatcher is able to keep its own "connection" as instance variable, but the connection might only be usable n times, have a lifetime or whatever. A nice example for this process can be seen in the reduction of code as done for
Progress: basic dispatcher pool configuration is now possible as of svn r5071 and should be configurable as
connector int property "maxDispatchersActive" (per endpoint). More maintenance (e.g. shrinking a pool to recover from peaks) will come later, I'd like to avoid the builtin pool reaper thread for that because the connector's scheduler is just more appropriate. I've added a separate issue (
Descoping the 1.4.1, unset Fix Version for some issues.
As the blocking JIRA has been closed this can follow as well, as the basic work has been completed for a long time now. Therefore setting fix-version to 1.4.1.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
MULE-237and closed as fixed..apparently too early.