一、实验前言
场景一:MQ集群负载均衡接收消息
从QMFX发送消息到MYCLUSTER集群(QMFX->MYCLUSTER),使消息能够负载分发到2个节点上,并且在发送过程中2个网关(QMRGRECGC其中任意一个宕机不影响正常发送。
目的:1.能够达到负载均衡;2.达到网关高可用
在MyCluster集群中两个网关队列管理器(QMRGRECGC)是双活的,都能提供正常服务,因此在发送端配置连接信息时,可以利用MQ8.0以上自带的主备切换性能达到数据传输的高可用。
高可用负载均衡MQ架构图:
二、高可用主备切换测试
先看发送端的MYCLUSTER.QLRECFX通道的配置,连接名称为192.168.153.129(1417),192.168.153.128(1417)
2.1 确认发送端通道连接信息
这样配置后就自动实现了优先连接192.168.153.129(1417),当第一个连接断开无法连接后会自动切换到192.168.153.128(1417)的连接。
通过MYCLUSTER.QLRECFX通道的通道状态查看当前连接的是192.168.153.129(1417)
进一步验证,在网关192.168.153.129上查看MYCLUSTER.QLRECFX接收通道的通道连接状态,可以看到192.168.153连接着。
网关192.168.153.129上查看MYCLUSTER.QLRECFX接收通道是RUNINNG状态
通道内的连接信息正是192.168.153.1
而此时另一个网关192.168.153.128:1417上的状态应该是“不活动”状态
注:不活动状态代表当前没有任务对象连接到该通道
2.2 启动消息发送
下一步启动消息发送,从QMFX端发送到MyCluster,在发送过程中强制中断当前连接的192.168.153.129:1417上的通道,看是否会自动切换到192.168.153.128:1417上。
2.3 停止主网关通道并观察发送端通道切换状态
手工停止192.168.153.129:1417上的MYCLUSTER.QLRECFX通道
然后观察发送端的通道连接情况
从这里看已正常切换到192.168.153.128:1417
在自动切换过程中发送端的通道会自动重试10次与原来的通道连接,若是不同之后再进行切换,切换过程中,传输队列中是禁止取数据的,稍微有点积压消息(积压的消息量视通道切换完成的时间有关),当切换完成发送通道为运行状态后,传输通道中的消息才会正常发送。
通道已切换成功,且把积压的消息发送完毕
重试的次数是可以自定义配置的,如下图
2.4 消息发送完毕确认负载均衡情况
程序设定发送100条报文后退出。
此时在Mycluster集群的两个数据节点上看看消息的分布情况。
MQPROD1上的QLRECENTRY本地队列当前队列深度为50
MQPROD2上的QLRECENTRY本地队列当前队列深度为50
三、结论
从上面可以看出两个节点的消息分布是均匀的,达到了负载均衡的效果,且在网关故障切换后没有发送数据丢失情况
附:发送消息测试代码
package com.fx.jmstest;
import java.io.IOException;
import com.ibm.mq.MQC;
import com.ibm.mq.MQEnvironment;
import com.ibm.mq.MQException;
import com.ibm.mq.MQGetMessageOptions;
import com.ibm.mq.MQMessage;
import com.ibm.mq.MQPutMessageOptions;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;
import com.ibm.mq.constants.CMQC;
import com.ibm.mq.constants.MQConstants;
public class Test {
static MQQueueManager qMgr;
static int CCSID = 1381;
static String queueString = "TO.GA";
public static void connect() throws MQException {
MQEnvironment.hostname = "192.168.153.1";// MQ服务器IP
MQEnvironment.CCSID = 1381; // 字符编码
MQEnvironment.port = 1414; // 队列管理器的端口号
// //MQ中拥有权限的用户名
MQEnvironment.userID = "MUSR_MQADMIN";
//用户名对应的密码
MQEnvironment.password = "123456";
try {
qMgr = new MQQueueManager("QMFX");// 队列管理器名称
} catch (MQException e) {
e.printStackTrace();
}
}
public static void sendMsg(String msgStr) {
//设置将要连接的队列属性
int openOptions = MQConstants.MQOO_OUTPUT | MQConstants.MQOO_FAIL_IF_QUIESCING;
/*关闭了就重新打开*/
MQQueue queue = null;
try {
// 建立Q1通道的连接
queue = qMgr.accessQueue(queueString, openOptions, null, null, null);
MQMessage msg = new MQMessage();// 要写入队列的消息
msg.format = MQConstants.MQFMT_STRING;
msg.characterSet = CCSID;
msg.encoding = CCSID;
// msg.writeObject(msgStr); //将消息写入消息对象中;
boolean Flag;
Flag=true;
int count=1;
while(Flag)
{
msg.writeString(msgStr);
MQPutMessageOptions pmo = new MQPutMessageOptions();
msg.expiry = -1; // 设置消息永不过期
queue.put(msg, pmo);// 将消息放入队列
System.out.println("已发送:"+count);
if(count==100){
//设置发送100条报文就停止发送
Flag=false;
}
count++;
Thread.sleep(2000);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (queue != null) {
try {
queue.close();
} catch (MQException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void receiveMsg() {
int openOptions = MQC.MQOO_INPUT_AS_Q_DEF | MQC.MQOO_OUTPUT | MQC.MQOO_INQUIRE;
MQQueue queue = null;
try {
queue = qMgr.accessQueue(queueString, openOptions, null, null, null);
System.out.println("该队列当前的深度为:" + queue.getCurrentDepth());
System.out.println("===========================");
int depth = queue.getCurrentDepth();
// 将队列的里的消息读出来
while (depth-- > 0) {
MQMessage msg = new MQMessage();// 要读的队列的消息
MQGetMessageOptions gmo = new MQGetMessageOptions();
queue.get(msg, gmo);
System.out.println("消息的大小为:" + msg.getDataLength());
System.out.println("消息的内容:\n" + msg.readStringOfByteLength(msg.getDataLength()));
System.out.println("---------------------------");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (queue != null) {
try {
queue.close();
} catch (MQException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws MQException {
connect();
sendMsg("我来测试一下");
// receiveMsg();
}
}