(九)IBMMQ集群与集群外的IBMMQ节点通讯 -集群外部对集群的同/异步消息 by java

上节按照生产环境的结构对集群进行了搭建,这节使用java代码对集群进行同步消息与异步消息j接收的测试

1. 集群节点接收消息的代码

为方便测试,添加配置文件工具类,读取配置文件顺序的优先级如下:1.程序打包后jar文件同级文件夹config下的config.properties文件。2.程序打包后jar文件的同级config.properties文件。3.classpath:config.properties

package com.qinke.mqcluster;

import com.ibm.mq.*;
import com.qinke.mqcluster.utils.ConfigUtil;

import java.io.IOException;

public class CCQMReceiver {
    private static String host;
    private static Integer port;
    private static String channel;
    private static String qmgr;
    private static String asyncQueueName;
    private static String syncQueueName;

    private static void init() {
        host = ConfigUtil.getConfig("HOST");
        port = Integer.valueOf(ConfigUtil.getConfig("PORT"));
        channel = ConfigUtil.getConfig("CHANNEL");
        qmgr = ConfigUtil.getConfig("QMGR");
        asyncQueueName = ConfigUtil.getConfig("ASYNCQUEUE");
        syncQueueName = ConfigUtil.getConfig("SYNCQUEUE");
        StringBuffer lisLog = new StringBuffer();
        lisLog.append(host)
                .append(" : ")
                .append(port)
                .append(" : ")
                .append(channel)
                .append(" : ")
                .append(qmgr)
                .append(" : ")
                .append(asyncQueueName)
                .append(" : ")
                .append(syncQueueName);
        //配置要使用的队列管理器的信息,这里使用队列管理器CQM1的信息
        MQEnvironment.hostname = host;
        MQEnvironment.port = port;
        MQEnvironment.channel = channel;
        System.out.println("初始化");
        System.out.println("监听参数:" + lisLog.toString());
    }

    //异步消息接收
    private static void asyncReceive() {
        try {
            //创建队列管理器对象,在实例化的时候会隐式连接队列管理器CQM1
            MQQueueManager mqQueueManager = new MQQueueManager(qmgr);
            //定义打开方式
            int openOption = MQC.MQOO_INPUT_SHARED; //以读取方式打开
            //直接循环,每次发10条消息
            //创建队列对量
            MQQueue queue = mqQueueManager.accessQueue(asyncQueueName, openOption);
            //定义获取消息时的一些操作
            MQGetMessageOptions gmo = new MQGetMessageOptions();
            gmo.options = gmo.options + MQC.MQGMO_WAIT; //无消息时等待
            gmo.options = gmo.options + MQC.MQGMO_FAIL_IF_QUIESCING; //队列管理器停止时退出等待
            gmo.waitInterval = 300000; //无消息时等待时长
            System.out.println("开始监听");
            for (int i = 0; i <= 1000; i++) {
                //创建简单消息对象
                MQMessage mqMessage = new MQMessage();
                queue.get(mqMessage, gmo);
                String msgContent = mqMessage.readUTF();
                System.out.println(msgContent);
            }
            //关闭打开的资源,养成好习惯
            queue.close();
            mqQueueManager.disconnect();
        } catch (MQException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //同步消息接收
    private static void syncReceive() {
        try {
            //创建队列管理器对象,在实例化的时候会隐式连接队列管理器CQM1
            MQQueueManager mqQueueManager = new MQQueueManager(qmgr);
            //定义打开方式
            int openOption = MQC.MQOO_INPUT_SHARED; //以读取方式打开
            //直接循环,每次发10条消息
            //创建队列对量
            MQQueue queue = mqQueueManager.accessQueue(syncQueueName, openOption);
            //定义获取消息时的一些操作
            MQGetMessageOptions gmo = new MQGetMessageOptions();
            gmo.options = gmo.options + MQC.MQGMO_SYNCPOINT;//在同步点控制下获取消息
            gmo.options = gmo.options + MQC.MQGMO_WAIT; //无消息时等待
            gmo.options = gmo.options + MQC.MQGMO_FAIL_IF_QUIESCING; //队列管理器停止时退出等待
            gmo.waitInterval = 300000; //无消息时等待时长
            /*
                这里为解决回执队列不同的情况,每次根据请求队列中携带的回执队列名称重新打开回执队列,若回执队列是确定的,可将回执队列的定义放到循环外,以减少队列开关次数来提高性能
             */
            for (int i = 0; i <= 1000; i++) {
                //创建简单消息对象
                MQMessage mqMessage = new MQMessage();
                //接收消息
                queue.get(mqMessage, gmo);
                //读取消息内容
                String msgContent = mqMessage.readUTF();
                System.out.println(msgContent);
                //判断,如果消息标识为请求消息,则进行回应
                if (mqMessage.messageFlags == MQC.MQMT_REQUEST) {
                    //获取回应队列
                    String replyQueueName = mqMessage.replyToQueueName;
                    //获取回应队列管理器
                    String replyQueueManager = mqMessage.replyToQueueManagerName;
                    //使用回应队列,回应队列管理器创建回执消息队列的对象
                    MQQueue sendQueue = mqQueueManager.accessQueue(replyQueueName, MQC.MQOO_OUTPUT | MQC.MQOO_FAIL_IF_QUIESCING, replyQueueManager, null, null);
                    //创建回执消息对象
                    MQMessage sendMsg = new MQMessage();
                    //设置消息对应参数,发送端使用此参数进行消息关联
                    sendMsg.correlationId = mqMessage.messageId;
                    //将请求消息内容写入响应消息(按照自己的业务写入相应的数据,测试时为测试方便,将消息回写)
                    sendMsg.writeUTF(msgContent);
                    //设置消息类型为响应消息
                    sendMsg.messageFlags = MQC.MQMT_REPLY;
                    //消息发送
                    sendQueue.put(sendMsg);
                    //关闭发送队列
                    sendQueue.close();
                }
            }
            //关闭打开的资源,养成好习惯
            queue.close();
            mqQueueManager.disconnect();
        } catch (MQException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        init();
        asyncReceive();
    }
}


2.集群外节点发送消息的代码

package com.qinke.mqcluster;

import com.ibm.mq.*;
import com.qinke.mqcluster.utils.ConfigUtil;

import java.io.IOException;

public class CCQMSender {
    private static String host;
    private static Integer port;
    private static String channel;
    private static String qmgr;
    private static String asyncQueueName;
    private static String syncQueueName;
    private static String repeictQueueName;

    private static void init() {
        host = ConfigUtil.getConfig("HOST");
        port = Integer.valueOf(ConfigUtil.getConfig("PORT"));
        channel = ConfigUtil.getConfig("CHANNEL");
        qmgr = ConfigUtil.getConfig("QMGR");
        asyncQueueName = ConfigUtil.getConfig("ASYNCQUEUE");
        syncQueueName = ConfigUtil.getConfig("SYNCQUEUE");
        repeictQueueName = ConfigUtil.getConfig("REPEICTQUEUENAME");
        StringBuffer lisLog = new StringBuffer();
        lisLog.append(host)
                .append(" : ")
                .append(port)
                .append(" : ")
                .append(channel)
                .append(" : ")
                .append(qmgr)
                .append(" : ")
                .append(asyncQueueName)
                .append(" : ")
                .append(syncQueueName)
                .append(" : ")
                .append(repeictQueueName);
        //配置要使用的队列管理器的信息,这里使用队列管理器CQM1的信息
        MQEnvironment.hostname = host;
        MQEnvironment.port = port;
        MQEnvironment.channel = channel;
        System.out.println("初始化");
        System.out.println("监听参数:" + lisLog.toString());
    }

    public static void sendAsync() {
        try {
            //创建队列管理器对象,在实例化的时候会隐式连接队列管理器CQM1
            MQQueueManager mqQueueManager = new MQQueueManager(qmgr);
            //定义打开方式
            int openOption = MQC.MQOO_OUTPUT; //以写入方式打开
            openOption = openOption + MQC.MQOO_BIND_NOT_FIXED;//写入消息方式为不绑定方式(想要负载均衡必须为此方式)
            //直接循环,每次发10条消息
            //创建队列对量
            MQQueue queue = mqQueueManager.accessQueue(asyncQueueName, openOption);
            for (int i = 0; i <= 10; i++) {
                //创建简单消息对象
                MQMessage mqMessage = new MQMessage();
                //将数据写入消息对象中(可自行尝试其他write方法)
                String msg = "简单消息:" + i;
                mqMessage.writeUTF(msg);
                //使用队列发送消息
                queue.put(mqMessage);
                System.out.println("send:" + msg);
            }
            //关闭打开的资源,养成好习惯
            queue.close();
            mqQueueManager.disconnect();
        } catch (MQException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void sendSync() {
        try {
            //创建队列管理器对象,在实例化的时候会隐式连接队列管理器CQM1
            MQQueueManager mqQueueManager = new MQQueueManager(qmgr);
            //定义打开方式
            int openOption = MQC.MQOO_OUTPUT; //以写入方式打开
            openOption = openOption + MQC.MQOO_BIND_NOT_FIXED;//写入消息方式为不绑定方式(想要负载均衡必须为此方式)
            //直接循环,每次发10条消息
            //创建队列对量
            MQQueue queue = mqQueueManager.accessQueue(syncQueueName, openOption);
            MQPutMessageOptions pmo = new MQPutMessageOptions();
            pmo.options = pmo.options + MQC.MQPMO_NEW_MSG_ID;
            pmo.options = pmo.options + MQC.MQPMO_SYNCPOINT;

            for (int i = 0; i <= 10; i++) {
                //创建简单消息对象
                MQMessage sendMsg = new MQMessage();
                //将数据写入消息对象中(可自行尝试其他write方法)
                sendMsg.writeUTF("简单消息:" + i);
                //设置消息标识为请求消息
                sendMsg.messageFlags = MQC.MQMT_REQUEST;
                //设置回执队列与回执队列管理器(当前队列管理器)
                sendMsg.replyToQueueName = repeictQueueName;
                sendMsg.replyToQueueManagerName = qmgr;
                //使用队列发送消息
                queue.put(sendMsg, pmo);
                byte[] messageId = sendMsg.messageId;
                //设置回执队列打开方式
                int getOpenOption = MQC.MQOO_INPUT_SHARED | MQC.MQOO_FAIL_IF_QUIESCING;
                //创建回执队列对象
                MQQueue mqQueue = mqQueueManager.accessQueue(repeictQueueName, getOpenOption);
                //创建获取消息的对象,并设置消息选择器
                MQMessage getMsg = new MQMessage();
                getMsg.correlationId = messageId;

                MQGetMessageOptions gmo = new MQGetMessageOptions();
                gmo.options = gmo.options + MQC.MQGMO_SYNCPOINT;//Get messages under sync point control(在同步点控制下获取消息)
                gmo.options = gmo.options + MQC.MQGMO_WAIT;  // Wait if no messages on the Queue(如果在队列上没有消息则等待)
                gmo.options = gmo.options + MQC.MQMO_MATCH_CORREL_ID;// Fail if Qeue Manager Quiescing(如果队列管理器停顿则失败)
                gmo.waitInterval = 2000;
                //获取响应消息
                mqQueue.get(getMsg, gmo);
                String getMsgContent = getMsg.readUTF();
                System.out.println(getMsgContent);
                mqQueue.close();

            }
            //关闭打开的资源,养成好习惯
            queue.close();
            mqQueueManager.disconnect();
        } catch (MQException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        init();
        sendAsync();
    }
}

3. 代码测试

3.1 异步消息发送与接收测试

在使用代码接收异步消息时,由于我们需要对集群中的三个节点进行监听,而IDE每次执行代码的进程只有一个,所以这里将代码进行打包后使用cmd运行。

3.1.1 代码打包方式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

构建完成之后在项目的根目录找到out文件夹,打包完成的程序在这个地方

在这里插入图片描述

将jar考走放到单独的文件夹中,按下shift再按下鼠标右键,点击在此处打开powershell窗口,即可在当前目录下打开cmd窗口,使用 java -jar .\mqcluster.jar 命令执行程序。

在这里插入图片描述

3.1.2 启动监听

注意: 打包前请确定接收程序的main方法调用的是否为异步消息接收的方法。打包后将jar文件复制到三个文件夹,文件夹下分别拷贝config.properties.想要当前程序监听哪个队列,只需修改配置文件即可。

在这里插入图片描述

三个节点异步消息监听启动完毕的效果
在这里插入图片描述

3.1.3 使用外部MQ节点向集群发送异步消息
  1. 将程序配置修改为CCQM6的相关配置
  2. CCQMSender的main方法调用修改为asyncSend
    在这里插入图片描述
  3. 启动CCQMSender.main,查看控制台的消息发送情况
    在这里插入图片描述
3.1.4 查看监听程序的消息接收情况

在这里插入图片描述

由图可以看出,异步消息正常发送且进行了负载均衡,集群外部对集群的异步消息发送与接收到此结束

3.2 同步消息的发送与接收

3.2.1 程序打包

程序打包方式同 3.1.1 。注意打包时将CCQMReceiver.main()中调用的方法修改为syncReceive();
在这里插入图片描述

3.2.2 启动监听

同 3.2.1。
在这里插入图片描述

3.1.3 使用外部MQ节点向集群发送同步消息
  1. 将程序配置修改为CCQM6的相关配置
  2. CCQMSender的main方法调用修改为syncSend
    在这里插入图片描述
    在这里插入图片描述
3.2.4 查看监听程序的消息接收情况

在这里插入图片描述

由图可以看出,同步消息到集群时也经过了负载均衡,且发送无问题,从集群外部到集群的同步消息测试到此结束。

3.2.5 附代码链接

4. 测试中发现的问题

集群结构是CCQM1主网关,CCQM2备网关,当与集群外部进行交互时,首选应为CCQM1,当网关CCQM1发生问题不能正常工作时,才进行切换CCQM2.
在测试中发现,从集群外部向集群发送同步消息时,入集群的消息全部都由CCQM1网关接收,但是出集群的消息,却是因为两个网关都有消息回执的集群共享队列,导致回执消息在两个网关上进行了负载均衡,使用过程中确实是没有什么问题,但回执消息的返回路径却跟想象的不太一样。当前还未找到解决办法,哪位大牛有相关的解决方案,可以在一块交流一下。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值