MUXPool Strategies
/by Barzilai Spinak a.k.a. @barspi a.k.a. The Facilitator/
Many jPOS users are familiar with the MUXPool
class. In short, it's a MUX
that sits in front of multiple muxes (usually QMUX
) and will choose among them using some configured strategy. The client code, such as a SelectDestination
participant in the transaction manager, will just talk to the MUXPool
(i.e., call the request()
method), without worrying about the underlying muxes, how many there are, whether they are available, etc.
Imagine you need to send messages to some issuer Bank ABC, and they have given you three connection endpoints (IPs and ports) for load balance or redundancy. In a typical configuration you would have three ChannelAdaptors
and three matching muxes: let's call them abc1
, abc2
, and abc2
. Your business logic will decide that "this transaction should go to Bank ABC", choosing a mux called BankABC
.
So, you could configure a MUXPool
with a nice symbolic name such as BankABC
like this:
<mux class="org.jpos.q2.iso.MUXPool" logger="Q2" name="BankABC">
<muxes>abc1 abc2 abc3</muxes>
<strategy>round-robin</strategy>
</mux>
In this example, it would choose among the three muxes using a round robin strategy, among the "usable" muxes. So, if abc2
is not usable at the moment, then the choice would happen between abc1
and abc3
(the concept of "usable" applies mostly to implementations that honor the isConnected()
method, such as QMUX
, but this is another topic for another time).
New tricks for an old dog
The built-in strategies for MUXPool
, which we won't discuss here, are:
round-robin
round-robin-with-override
split-by-divisor
- and the default, internally called
PRIMARY_SECONDARY
, which basically goes through the list in order until it finds the first usable mux.
Those strategies have existed for ages (git
is telling me "9 years ago" for the latest addition of some of them). But for some time now, since around april 2023, there's a new trick: The inner interface MUXPool.StrategyHandler
.
This is what it looks like, right from the jPOS source code:
public interface StrategyHandler {
/** If this method returns null, the {@link MUXPool} will fall back to the configured built-in
* strategy.
*
* @param pool the {@link MUXPool} using this strategy handler
* @param m the {@link ISOMsg} that we wish to send
* @param maxWait deadline in milliseconds (epoch value as given by {@code System.currentTimeMillis()})
* @return an appropriate {@link MUX} for this strategy, or {@code null} if none is found
*/
MUX getMUX(MUXPool pool, ISOMsg m, long maxWait);
}
If the Javadoc is not clear enough, an instance of StrategyHandler
will be called by the MUXPool
, passing a reference to itself (pool
), the ISOMsg
we intend to send, and a timeout for the response.
This is how you could configure your MUXPool
with a StrategyHandler
:
<mux class="org.jpos.q2.iso.MUXPool" logger="Q2" name="BankABC">
<muxes>abc1 abc2 abc3</muxes>
<!-- The default, fall-back strategy -->
<strategy>round-robin</strategy>
<strategy-handler class="xxx.yyy.MyPoolStrategy">
<!-- some config properties here -->
</ strategy-handler>
</mux>
So, you implement the MyPoolStrategy
, and your getMUX()
method can choose among the available muxes using some other strategy. It's convenient to invoke the String[] getMuxNames()
method of the given pool
, and then you could use the NameRegistrar
to get a reference to the muxes. You then could, for example, query some metrics from the underlying muxes (such as the ones provided by QMUX
) to make your decision of the best mux to use.
If your _ StrategyHandler
_ can't decide, then you just return null
and the configured <strategy>
will be used as a fall-back.
Some ideas for strategies include:
- Use the
getRxPending()
method ofQMUX
to get an estimate of which mux is more loaded, or slower to respond. - Use the
getIdleTimeInMillis()
ofQMUX
to find a mux that hasn't been used recently. - Take a look at the MTI of the
ISOMsg
to send reversals to a different destination from authorizations. - A weighted round-robin, perhaps using some custom property of your muxes (such as subclass of
QMUX
), or configuring the weights in the strategy handler itself (hint: they can beConfigurable
so they can have their own configuration properties in the XML)
There may be alternate ways to implement similar behavior using other jPOS components, but the MUXPool.StrategyHandler
may be a nice trick to keep in mind, and make some logic simpler.
Your imagination is the limit!