Skip to main content

MUXPool Tutorial

Where to work

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

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.

Tutorial simplification

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 / PropertyDescription
<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-enabledWhen 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>