场景三:测试高可用,使用连接工厂(多地址连接方式),不通过虚地址实现单节点故障后自动连接备用节点(QMRGRECGC->QMFX)
目的:1.实现主节点故障后能够自动重连到备用节点实现消息不丢失,服务高可用
该架构图与实验二的架构图在两个网关前减少了HA的高可用软件。
具体不一样的是场景三不使用Keepalived作为高可用服务,而采用IBM MQ提供的连接工厂方式,实现程序的主备切换。
各个队列管理的配置跟实验场景二一样,这里不再重复。
在程序运行过程中停止主网关的客户端连接通道MY.SVRCONN
已发送:15
已发送:16
已发送:17
已发送:18
====有异常=====
com.ibm.msg.client.jms.DetailedJMSException: JMSWMQ2007: 将消息发送至目标 'TO.HUIZHI' 失败。
JMS 尝试执行 MQPUT 或 MQPUT1;然而 WebSphere MQ 报告了错误。
使用链接的异常来确定此错误的原因。
at com.ibm.msg.client.wmq.common.internal.Reason.reasonToException(Reason.java:578)
at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:214)
at com.ibm.msg.client.wmq.internal.WMQMessageProducer.checkJmqiCallSuccess(WMQMessageProducer.java:1157)
at com.ibm.msg.client.wmq.internal.WMQMessageProducer.checkJmqiCallSuccess(WMQMessageProducer.java:1114)
at com.ibm.msg.client.wmq.internal.WMQMessageProducer.access$800(WMQMessageProducer.java:72)
at com.ibm.msg.client.wmq.internal.WMQMessageProducer$SpiIdentifiedProducerShadow.sendInternal(WMQMessageProducer.java:843)
at com.ibm.msg.client.wmq.internal.WMQMessageProducer$ProducerShadow.send(WMQMessageProducer.java:521)
at com.ibm.msg.client.wmq.internal.WMQMessageProducer.send(WMQMessageProducer.java:1283)
at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.sendMessage(JmsMessageProducerImpl.java:838)
at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.synchronousSendInternal(JmsMessageProducerImpl.java:1967)
at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.sendInternal(JmsMessageProducerImpl.java:1913)
at com.ibm.msg.client.jms.internal.JmsMessageProducerImpl.send(JmsMessageProducerImpl.java:1469)
at com.ibm.mq.jms.MQMessageProducer.send(MQMessageProducer.java:293)
at com.datatech.jmstest.Test_CF.sendMsg2(Test_CF.java:202)
at com.datatech.jmstest.Test_CF.main(Test_CF.java:171)
Caused by: com.ibm.mq.MQException: JMSCMQ0001: WebSphere MQ 调用失败,完成代码为 '2' ( 'MQCC_FAILED' ),原因为 '2009' ( 'MQRC_CONNECTION_BROKEN' )。
at com.ibm.msg.client.wmq.common.internal.Reason.createException(Reason.java:202)
... 13 more
Caused by: com.ibm.mq.jmqi.JmqiException: CC=2;RC=2009
at com.ibm.mq.jmqi.remote.impl.RemoteSession.getConnection(RemoteSession.java:494)
at com.ibm.mq.jmqi.remote.impl.RemoteSession.getMaximumMessageLength(RemoteSession.java:1802)
at com.ibm.mq.jmqi.remote.api.RemoteFAP.jmqiPutMessageWithProps(RemoteFAP.java:9860)
at com.ibm.mq.jmqi.remote.api.RemoteFAP.jmqiPut(RemoteFAP.java:8968)
at com.ibm.mq.ese.jmqi.InterceptedJmqiImpl.jmqiPut(InterceptedJmqiImpl.java:624)
at com.ibm.mq.ese.jmqi.ESEJMQI.jmqiPut(ESEJMQI.java:636)
at com.ibm.msg.client.wmq.internal.WMQMessageProducer$SpiIdentifiedProducerShadow.sendInternal(WMQMessageProducer.java:831)
... 9 more
Caused by: com.ibm.mq.jmqi.JmqiException: CC=2;RC=2202
at com.ibm.mq.jmqi.remote.impl.RemoteRcvThread.run(RemoteRcvThread.java:441)
at com.ibm.msg.client.commonservices.workqueue.WorkQueueItem.runTask(WorkQueueItem.java:263)
at com.ibm.msg.client.commonservices.workqueue.SimpleWorkQueueItem.runItem(SimpleWorkQueueItem.java:99)
at com.ibm.msg.client.commonservices.workqueue.WorkQueueItem.run(WorkQueueItem.java:284)
at com.ibm.msg.client.commonservices.workqueue.WorkQueueManager.runWorkQueueItem(WorkQueueManager.java:312)
at com.ibm.msg.client.commonservices.j2se.workqueue.WorkQueueManagerImplementation$ThreadPoolWorker.run(WorkQueueManagerImplementation.java:1193)
已发送:19
已发送:20
已发送:21
已发送:22
已发送:23
已发送:24
已发送:25
已发送:26
已发送:27
已发送:28
已发送:29
发动100个消息后程序停止,在主网关故障切换到备网关后,消息发送没有发生丢失,并正常发送。
使用连接工厂方式可以减少高可用架构时候不用第三方的高可用软件,借用IBM MQ自身的方式就可以达到效果,减少一个环节就少一份风险,该代码还有很多优化的空间,本次只是测试是否能达到高可用服务状态,通过实验证明可以达到要求。
该代码中使用的是com.ibm.mq.jms.MQQueueConnectionFactory连接类
package com.datatech.jmstest;
import java.io.IOException;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.QueueConnection;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
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;
import com.ibm.mq.jms.MQQueueConnectionFactory;
import com.ibm.msg.client.wmq.WMQConstants;
public class Test_CF {
static MQQueueManager qMgr;
static int CCSID = 1381;
static String queueString = "TO.HUIZHI";
static int openOptions = MQConstants.MQOO_OUTPUT | MQConstants.MQOO_FAIL_IF_QUIESCING;
static MQQueue queue = null;
public static void connect() throws MQException {
MQEnvironment.hostname = "192.168.153.200";// MQ服务器IP
MQEnvironment.channel = "MY.SVRCONN"; // 队列管理器对应的服务器连接通道
MQEnvironment.CCSID = 1381; // 字符编码
MQEnvironment.port = 1417; // 队列管理器的端口号
// //MQ中拥有权限的用户名
MQEnvironment.userID = "mqm";
//用户名对应的密码
MQEnvironment.password = "123456";
try {
qMgr = new MQQueueManager("QMRGRECGC");// 队列管理器名称
queue = qMgr.accessQueue(queueString, openOptions, null, null, null);
} catch (MQException e) {
e.printStackTrace();
}
}
public static void sendMsg(String msgStr) {
//设置将要连接的队列属性
//目标为远程队列,所有这里不可以用MQOO_INPUT_AS_Q_DEF属性
//int openOptions = MQC.MQOO_INPUT_AS_Q_DEF | MQC.MQOO_OUTPUT;
//以下选项可适合远程队列与本地队列
//连接队列
//MQQueue provides inquire, set, put and get operations for WebSphere MQ queues.
//The inquire and set capabilities are inherited from MQManagedObject.
/*关闭了就重新打开*/
// 建立Q1通道的连接
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)
{
try {
MQPutMessageOptions pmo = new MQPutMessageOptions();
msg.expiry = -1; // 设置消息用不过期
msg.writeString(msgStr);
queue.put(msg, pmo);// 将消息放入队列
}catch(java.net.SocketException e){
System.out.println("网络故障,正在切换备用服务");
} catch(IOException e){
System.out.println("IO故障");
//msg.writeString(msgStr);
}
catch (MQException e) {
System.out.println("===="+e.getCompCode()+"============");
System.out.println("====网络故障,正在切换备用服务============");
try {
connect();
MQPutMessageOptions pmo = new MQPutMessageOptions();
msg.expiry = -1; // 设置消息用不过期
try {
msg.writeString(msgStr);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
queue.put(msg, pmo);// 将消息放入队列
} catch (MQException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
}
System.out.println("已发送:"+count);
count++;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
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();
sendMsg2("我来测试一下");
// receiveMsg();
}
public static void sendMsg2(String msg)
{
MQQueueConnectionFactory connectionFactory = null;
QueueConnection queueConn = null;
QueueSession queueSession = null;
QueueSender queueSender = null;
TextMessage message = null;
try
{
System.out.println("===进入========");
connectionFactory = new MQQueueConnectionFactory();
connectionFactory.setConnectionNameList("192.168.153.128(1417), 192.168.153.129(1417)");
connectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
connectionFactory.setQueueManager("QMRGRECGC");
connectionFactory.setChannel("MY.SVRCONN");
queueConn = connectionFactory.createQueueConnection("mqm","123456");
queueSession = queueConn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
queueSender = queueSession.createSender(queueSession.createQueue("TO.HUIZHI"));
queueSender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
message = queueSession.createTextMessage(msg);
message.setJMSCorrelationID("12345");
int count=1;
while(true){
try{
queueSender.send(message);
System.out.println("已发送:"+count);
count++;
Thread.sleep(2000);
} catch (Exception e)
{
System.out.println("====有异常=====");
queueConn = connectionFactory.createQueueConnection("mqm","123456");
queueSession = queueConn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
queueSender = queueSession.createSender(queueSession.createQueue("TO.HUIZHI"));
queueSender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
message = queueSession.createTextMessage(msg);
message.setJMSCorrelationID("12345");
queueSender.send(message);
System.out.println("已发送:"+count);
count++;
}
}
}
catch (Exception e)
{
e.printStackTrace();
}finally{
if (queue != null) {
try {
queueSender.close();
queue.close();
} catch (MQException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}