This article describes the key steps to configure an application running on the WebSphere Liberty server to put a message into the cluster queue of an MQ queue manager. A queue manager cluster is a network of queue managers, interconnected to fulfill a business function. Queue managers within the cluster can communicate with one another without the need to define sender/receiver channels or remote-queue definitions - which simplifies administration tasks. In addition, when configured in a gateway queue manager pattern, it does load balancing to two or more queue managers.
Prerequisites
- IBM MQ Resource Adapter 9.3.0.1-IBM-MQ-Java-InstallRA.jar (download from IBM Fix Central).
- Docker
- Docker Compose
- curl
- Visual Studio Code
OverviewNote: For setup convenience, I will not be installing MQ and WebSphere Liberty, but instead use the IBM-certified images from IBM repositories. I will be using docker-compose to run the demo. The diagram below shows the logical architecture.
Some notes about the setup:
- There are 3 queue managers configured in the queue manager cluster called AUS.
- CANBERRA is configured as a gateway queue manager and a partial repository.
- SYDNEY is configured as a queue manager that hosts a cluster queue called KANGAROO.Q. It is also a full repository.
- MELBOURNE is configured as a queue manager that hosts a cluster queue called KANGAROO.Q. It is also a full repository.
- There is an application mdbtest configured with the following:
- JMS code sends a message to the cluster queue while connecting the CANBERRA queue manager.
- Message drive bean MDBQM1 receives a message from KANGAROO.Q queue on SYDNEY queue manager.
- Message drive bean MDBQM2 receives a message from KANGAROO.Q queue on MELBOURNE queue manager.
Configuration FilesThe configuration can be found in this Git repository https://github.com/khongks/ws-liberty-mq-cluster. I will walk through the key configurations.
mdbtest/src/main/liberty/config/server.xml
- JMS Activation Specification referencing a CCDT file ccdt-sydney.json. Here, we need to specify the actual queue manager name (SYDNEY) in the queueManager field.
<jmsActivationSpec authDataRef="mqAuth" id="mdbtest/SampleListenerMDBQM1">
<properties.wmqJms
ccdtURL="file://${server.config.dir}ccdt-sydney.json"
queueManager="SYDNEY"
destinationRef="jms/queue1"/>
</jmsActivationSpec>
- JMS Activation Specification referencing a CCDT file ccdt-melbourne.json. Here, we need to specify the actual queue manager name (MELBOURNE) in the queueManager field.
<jmsActivationSpec authDataRef="mqAuth" id="mdbtest/SampleListenerMDBQM2">
<properties.wmqJms
ccdtURL="file://${server.config.dir}ccdt-melbourne.json"
queueManager="MELBOURNE"
destinationRef="jms/queue1"/>
</jmsActivationSpec>
mdbtest/src/main/liberty/config/ccdt-canberra.json
- This is the CCDT file that is used to connect to the CANBERRA queue manager. Note that in Docker networking, we can use the container name as the hostname. You will see this is how we specify this in our docker-compose file.
{
"channel": [
{
"name": "APP.SVRCONN",
"clientConnection": {
"connection": [
{
"host": "canberra",
"port": 1414
}
],
"queueManager": " "
},
"type": "clientConnection"
}
]
}
mdbtest/src/main/liberty/config/ccdt-sydney.json
- This is the CCDT file that is used to connect to the SYDNEY queue manager.
{
"channel": [
{
"name": "APP.SVRCONN",
"clientConnection": {
"connection": [
{
"host": "sydney",
"port": 1414
}
],
"queueManager": "SYDNEY"
},
"type": "clientConnection"
}
]
}
mdbtest/src/main/liberty/config/ccdt-melbourne.json
- This is the CCDT file that is used to connect to the MELBOURNE queue manager.
{
"channel": [
{
"name": "APP.SVRCONN",
"clientConnection": {
"connection": [
{
"host": "melbourne",
"port": 1414
}
],
"queueManager": "MELBOURNE"
},
"type": "clientConnection"
}
]
}
ibmmq/CANBERRA/config.mqsc
- One cluster receiver channel AUS.CANBERRA.
- Two cluster sender channels AUS.SYDNEY connecting to SYDNEY queue manager and AUS.MELBOURNE connecting to MELBOURNE queue manager.
- One server connection channel APP.SVRCONN for the application to connect to.
DEF CHL(AUS.CANBERRA) CHLTYPE(CLUSRCVR) CONNAME('canberra(1414)') CLUSTER(AUS)
DEF CHL(AUS.SYDNEY) CHLTYPE(CLUSSDR) CONNAME('sydney(1414)') CLUSTER(AUS)
DEF CHL(AUS.MELBOURNE) CHLTYPE(CLUSSDR) CONNAME('melbourne(1414)') CLUSTER(AUS)
DEF CHL(APP.SVRCONN) CHLTYPE(SVRCONN) MCAUSER('app') REPLACE
SET CHLAUTH('*') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(NOACCESS) DESCR('BackStop rule')
SET CHLAUTH(AUS.CANBERRA) TYPE(BLOCKUSER) USERLIST('nobody') DESCR('Allow privileged users on this channel')
SET CHLAUTH(AUS.CANBERRA) TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) CHCKCLNT(ASQMGR)
SET CHLAUTH(APP.SVRCONN) TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) CHCKCLNT(ASQMGR)
ALTER AUTHINFO(SYSTEM.DEFAULT.AUTHINFO.IDPWOS) AUTHTYPE(IDPWOS) ADOPTCTX(YES)
REFRESH SECURITY TYPE(CONNAUTH)
SET AUTHREC OBJTYPE(QUEUE) PROFILE(SYSTEM.CLUSTER.TRANSMIT.QUEUE) PRINCIPAL('app') AUTHRMV(ALL) AUTHADD(PUT,GET,BROWSE,INQ)
ibmmq/SYDNEY/config.mqsc
- Configured as a full repository.
- One cluster receiver channel AUS.SYDNEY.
- One cluster sender channels AUS.MELBOURNE connecting to MELBOURNE queue manager (peer full repository).
- One local queue KANGAROO.Q configured as a cluster queue.
- One server connection channel APP.SVRCONN for the application to connect to.
ALTER QMGR REPOS(AUS)
DEF CHL(AUS.SYDNEY) CHLTYPE(CLUSRCVR) CONNAME('sydney(1414)') CLUSTER(AUS)
DEF CHL(AUS.MELBOURNE) CHLTYPE(CLUSSDR) CONNAME('melbourne(1414)') CLUSTER(AUS)
DEF CHL(APP.SVRCONN) CHLTYPE(SVRCONN) MCAUSER('app') REPLACE
DEF QL(KANGAROO.Q) CLUSTER(AUS) DEFBIND(NOTFIXED)
SET CHLAUTH('*') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(NOACCESS) DESCR('BackStop rule')
SET CHLAUTH(AUS.SYDNEY) TYPE(BLOCKUSER) USERLIST('nobody') DESCR('Allow privileged users on this channel')
SET CHLAUTH(AUS.SYDNEY) TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) CHCKCLNT(ASQMGR)
SET CHLAUTH(APP.SVRCONN) TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) CHCKCLNT(ASQMGR)
ALTER AUTHINFO(SYSTEM.DEFAULT.AUTHINFO.IDPWOS) AUTHTYPE(IDPWOS) ADOPTCTX(YES)
REFRESH SECURITY TYPE(CONNAUTH)
SET AUTHREC OBJTYPE(QUEUE) PROFILE(KANGAROO.Q) PRINCIPAL('app') AUTHRMV(ALL) AUTHADD(PUT,GET,BROWSE,INQ)
ibmmq/MELBOURNE/config.mqsc
- Configured as a full repository.
- One cluster receiver channel AUS.MELBOURNE.
- One cluster sender channels AUS.SYDNEY connecting to SYDNEY queue manager (peer full repository).
- One local queue KANGAROO.Q configured as a cluster queue.
- One server connection channel APP.SVRCONN for the application to connect to.
ALTER QMGR REPOS(AUS)
DEF CHL(AUS.MELBOURNE) CHLTYPE(CLUSRCVR) CONNAME('melbourne(1414)') CLUSTER(AUS)
DEF CHL(AUS.SYDNEY) CHLTYPE(CLUSSDR) CONNAME('sydney(1414)') CLUSTER(AUS)
DEF CHL(APP.SVRCONN) CHLTYPE(SVRCONN) MCAUSER('app') REPLACE
DEF QL(KANGAROO.Q) CLUSTER(AUS) DEFBIND(NOTFIXED)
SET CHLAUTH('*') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(NOACCESS) DESCR('BackStop rule')
SET CHLAUTH(AUS.MELBOURNE) TYPE(BLOCKUSER) USERLIST('nobody') DESCR('Allow privileged users on this channel')
SET CHLAUTH(AUS.MELBOURNE) TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) CHCKCLNT(ASQMGR)
SET CHLAUTH(APP.SVRCONN) TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) CHCKCLNT(ASQMGR)
ALTER AUTHINFO(SYSTEM.DEFAULT.AUTHINFO.IDPWOS) AUTHTYPE(IDPWOS) ADOPTCTX(YES)
REFRESH SECURITY TYPE(CONNAUTH)
SET AUTHREC OBJTYPE(QUEUE) PROFILE(KANGAROO.Q) PRINCIPAL('app') AUTHRMV(ALL) AUTHADD(PUT,GET,BROWSE,INQ)
There are four Dockerfile(s), and each is used to build the image.
- ibmmq/CANBERRA/Docker is used to build the canberra image used to run the CANBERRA queue manager.
- ibmmq/SYDNEY/Docker is used to build the sydney image used to run the SYDNEY queue manager.
- ibmmq/MELBOURNE/Dockerfile is used to build the melbourne image used to run the MELBOURNE queue manager.
- mdbtest/Dockerfile is used to build the mdbtest image used to run the WebSphere Liberty application.
- compose.yaml is a docker-compose file used to run all the containers in the demo.
Steps to run the demo
- Build all the images.
% cd ibmmq/CANBBERA
% ./build.sh
% cd ibmmq/SYDNEY
% ./build.sh
% cd ibmmq/MELBOURNE
% ./build.sh
% cd mdbtest
% docker build -t mdbtest .
- Verify all the images are built
% docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mdbtest latest e7c251230d01 2 days ago 1.11GB
canberra latest 4b50596ac4dc 3 days ago 833MB
melbourne latest c26b4a8ead0a 3 days ago 833MB
sydney latest 89fc9db0d04f 3 days ago 833MB
- To run all the containers, we used Docker Compose.
% docker-compose up -d
Creating network "ws-liberty-mq-cluster_mq-cluster" with the default driver
Creating sydney ... done
Creating melbourne ... done
Creating canberra ... done
Creating mdbtest ... done
- To check if all the containers are runnings
% docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3fdbbe1e93cd mdbtest "/opt/ibm/helpers/ru…" About a minute ago Up About a minute 0.0.0.0:9080->9080/tcp, :::9080->9080/tcp, 0.0.0.0:10443->9443/tcp, :::10443->9443/tcp mdbtest
166ad3bc1b45 canberra "runmqdevserver" About a minute ago Up About a minute 0.0.0.0:1414->1414/tcp, :::1414->1414/tcp, 0.0.0.0:9443->9443/tcp, :::9443->9443/tcp, 9157/tcp canberra
d7ea4105ccc5 melbourne "runmqdevserver" About a minute ago Up About a minute 9157/tcp, 0.0.0.0:1416->1414/tcp, :::1416->1414/tcp, 0.0.0.0:9445->9443/tcp, :::9445->9443/tcp melbourne
8e435b0ba444 sydney "runmqdevserver" About a minute ago Up About a minute 9157/tcp, 0.0.0.0:1415->1414/tcp, :::1415->1414/tcp, 0.0.0.0:9444->9443/tcp, :::9444->9443/tcp sydney
- To check and tail the logs
% docker-compose logs -f
- To test, open another terminal window and run the test script sendmessage.sh, specify the number of messages to put.
% cd test
./sendmessage.sh 100
- From the logs, you will find that the messages are received alternately from each of the queue managers SYDNEY and MELBOURNE.
mdbtest | Message enqueued.
mdbtest | MDB received from QM1: test message #1
mdbtest | Message enqueued.
mdbtest | MDB received from QM1: test message #2
mdbtest | Message enqueued.
mdbtest | MDB received from QM2: test message #3
mdbtest | Message enqueued.
mdbtest | MDB received from QM1: test message #4
mdbtest | Message enqueued.
...
...
...
...
mdbtest | Message enqueued.
mdbtest | MDB received from QM1: test message #99
mdbtest | Message enqueued.
mdbtest | MDB received from QM2: test message #100