<>一、实验前言

场景二:测试高可用HA下绑定VIP地址做IP地址漂移(QMRGRECGC->QMFX),实现在单节点故障情况下,自动虚IP漂移到另一个节点上实现服务高可用
目的:1.虚IP在2个节点间自动漂移;2.VIP漂移后应用程序能够自动重连实现消息重复不丢失,服务高可用

回执发送在两个网关管理器前面加了一层高可用Keepalived来实现故障IP切换,对程序连接是透明的。

多个网关队列管理器做HA,绑定VIP实现在回执发送过程中因主网关队列管理器宕机后,还能继续发送回执给远端MQ。

本场景实验可以和场景一的实验一起合并后形成一个完整的从远端发送消息到接收端集群,然后通过消息消费处理后又返回回执结果给远端队列管理器,这个过程中涉及到远端发送消息的高可用和本地集群发送回执的高可用,而由于远端发送消息的高可用在实验一中已测试,本实验只测试第二个步骤本地集群发送回执的高可用。

HA VIP:192.168.153.200

HA采用手工切换方式,利用LInux的绑定虚IP命令
ifconfig eth0:1 192.168.153.200 netmask 255.255.255.0 up ifconfig eth0:1 down
<>二、实验步骤

<>2.1HA VIP绑定

首先在主网关上绑定VIP
绑定前要确认网卡的名字

192.168.153.129的网卡为eth1,因此执行
ifconfig eth1:1 192.168.153.200 netmask 255.255.255.0 up

<>2.2确认Mycluster的网关与回执接收端的通道状态

192.168.153.129 QMRGRECGC连接远端MQ

192.168.153.128 QMRGRECGC连接远端MQ

192.168.153.1回执接收端MQFX的通道状态

通过以上确认各个通道都已启用,下面启动回执发送程序。

<>2.3启动回执发送程序

<>2.4模拟主网关故障,HA切换

在主网关上停止VIP地址绑定
ifconfig eth1:1 down

然后在备用网关上绑定VIP地址

执行绑定命令必须在root用户下执行

切换过程中观察代码的异常捕捉

<>2.5切换过程中的程序异常捕捉
已发送:18 已发送:19 已发送:20 已发送:21 ====2============ ====网络故障,正在切换备用服务============
com.ibm.mq.MQException: MQJE001: 完成代码为 '2',原因为 '2009'。 at
com.ibm.mq.MQDestination.internalMQPUT(MQDestination.java:1304) at
com.ibm.mq.MQDestination.put(MQDestination.java:1152) at
com.datatech.jmstest.Test_HUIZHI.sendMsg(Test_HUIZHI.java:71) at
com.datatech.jmstest.Test_HUIZHI.main(Test_HUIZHI.java:164) Caused by:
com.ibm.mq.jmqi.JmqiException: CC=2;RC=2009 at
com.ibm.mq.jmqi.remote.impl.RemoteSession.receiveAsyncTsh(RemoteSession.java:617)
at com.ibm.mq.jmqi.remote.impl.RemoteSession.receiveTSH(RemoteSession.java:796)
at
com.ibm.mq.jmqi.remote.impl.RemoteSession.receiveMQIFlow(RemoteSession.java:1425)
at
com.ibm.mq.jmqi.remote.api.RemoteFAP.jmqiPutMessageWithProps(RemoteFAP.java:10025)
at com.ibm.mq.jmqi.remote.api.RemoteFAP.MQPUT(RemoteFAP.java:8877) at
com.ibm.mq.ese.jmqi.InterceptedJmqiImpl.MQPUT(InterceptedJmqiImpl.java:679) at
com.ibm.mq.ese.jmqi.ESEJMQI.MQPUT(ESEJMQI.java:491) at
com.ibm.mq.MQDestination.internalMQPUT(MQDestination.java:1297) ... 3 more
Caused by: com.ibm.mq.jmqi.JmqiException: CC=2;RC=2009;AMQ9213: 发生 'TCP' 的通信错误。
[1=java.net.SocketException[Connection reset],4=TCP,5=sockInStream.read] at
com.ibm.mq.jmqi.remote.impl.RemoteTCPConnection.receive(RemoteTCPConnection.java:1540)
at
com.ibm.mq.jmqi.remote.impl.RemoteRcvThread.receiveBuffer(RemoteRcvThread.java:786)
at
com.ibm.mq.jmqi.remote.impl.RemoteRcvThread.receiveOneTSH(RemoteRcvThread.java:749)
at com.ibm.mq.jmqi.remote.impl.RemoteRcvThread.run(RemoteRcvThread.java:141) 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)
Caused by: java.net.SocketException: Connection reset at
java.net.SocketInputStream.read(SocketInputStream.java:210) at
java.net.SocketInputStream.read(SocketInputStream.java:141) at
com.ibm.mq.jmqi.remote.impl.RemoteTCPConnection.receive(RemoteTCPConnection.java:1530)
... 8 more 已发送:22 已发送:23 已发送:24 已发送:25
在代码中捕捉到异常后,发起重连,从代码中看发送消息的数量没有跳跃,没有丢失,然后在MQFX回执接收端查看当前队列深度也为100,消息没有丢失。

<>结论

通过HA可以使用多个网关下对程序透明情况下的自动切换发送消息到远端,但程序中要注意的是需要捕捉到连接的异常,并进行判断然后发起通道的重连,若是程序没有异常处理环节则就达不到高可用的服务状态。

<>附:回执发送远程队列代码
package com.datatech.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_HUIZHI { 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); if(count==100){ Flag=false; }
count++; try { Thread.sleep(5000); } 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 { System.out.println("========回执高可用服务发送测试========");
connect(); sendMsg("我来测试一下"); // receiveMsg(); } }

技术
下载桌面版
GitHub
百度网盘(提取码:draw)
Gitee
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:766591547
关注微信