MUXPool Tutorial
The code for this tutorial lives under tutorials/muxpool. It requires the qserver tutorial running on port 10000:
# Terminal 1 — server
./gradlew :tutorials:qserver:installApp
./tutorials/qserver/build/install/qserver/bin/q2
# Terminal 2 — pool
./gradlew :tutorials:muxpool:installApp
./tutorials/muxpool/build/install/muxpool/bin/q2
The single-link problem
A single QMUX is a single point of failure. If the upstream host becomes unreachable or the TCP connection drops mid-transaction, there is no fallback.
MUXPool solves this by implementing the MUX interface itself — so it is a drop-in replacement from the application's perspective — while internally managing a list of QMUXes and selecting one on each request() call according to a configurable strategy.
Both channels in this tutorial connect to the same QServer instance on port 10000, establishing two independent TCP connections. In production, each channel would connect to a different upstream host or network interface.
Deploy file layout
10_channel_a.xml ← ChannelAdaptor "upstream-a" (10_xxx)
10_channel_b.xml ← ChannelAdaptor "upstream-b" (10_xxx)
20_qmux_a.xml ← QMUX "upstream-a" (20_xxx)
20_qmux_b.xml ← QMUX "upstream-b" (20_xxx)
25_pool.xml ← MUXPool "upstream" (25_xxx)
30_logon_manager.xml ← LogonManager (30_xxx)
50_send_request.xml ← SendRequest using the pool (50_xxx)
MUXPool XML descriptor
<mux class="org.jpos.q2.iso.MUXPool" logger="Q2" name="upstream">
<muxes>upstream-a upstream-b</muxes>
<strategy>round-robin</strategy>
<property name="check-enabled" value="true" />
</mux>
| Element / Property | Description |
|---|---|
<muxes> | Space-separated list of QMUX names. Any name not found in NameRegistrar at startup is logged and silently dropped — the pool starts with whatever is available. |
<strategy> | Selection strategy. See Strategies for the full list. |
check-enabled | When true, a MUX is only eligible for traffic if a <mux-name>.enabled entry exists in Space with the same object reference as the ready semaphore. See check-enabled. |
LogonManager descriptor
LogonManager manages the individual QMUXes, not the pool. The pool is registered under mux.upstream in NameRegistrar; LogonManager manages mux.upstream-a and mux.upstream-b directly:
<logon-manager class="org.jpos.tutorial.LogonManager" logger="Q2" name="logon-manager">
<property name="mux" value="upstream-a" />
<property name="mux" value="upstream-b" />
<property name="check-enabled" value="true" /> <!-- publish enabled key -->
...
</logon-manager>
isConnected() vs isUsable()
MUXPool.isConnected() returns true if any member MUX reports isConnected(). It is used by application code to check whether the pool can route traffic at all.
isUsable() is the internal per-MUX check run on every request() call. Without check-enabled it delegates to mux.isConnected(). With check-enabled=true it additionally verifies the enabled semaphore (see check-enabled).
Build and run
./gradlew :tutorials:muxpool:installApp
./tutorials/muxpool/build/install/muxpool/bin/q2
With one QServer running you will see two simultaneous logon sequences:
<log realm="logon-manager-upstream-a" at="...">
<info>Logon OK — upstream-a (session=...)</info>
</log>
<log realm="logon-manager-upstream-b" at="...">
<info>Logon OK — upstream-b (session=...)</info>
</log>
Then SendRequest fires every 60 seconds through the pool and the round-robin strategy alternates between the two links:
<log realm="send-request" at="...">
<info>sending 2800 via pool 'upstream', STAN=000001</info>
</log>
<log realm="send-request" at="...">
<info>response 2810, STAN=000001 rc=0000</info>
</log>