SpringBoot整合IBM MQ,多个队列管理器通信

 

说明:本项目开发前提是,在本地windows机器上搭建了MQ,并建立好发送队列管理器,本地队列,远程队列,发送通道;在阿里云服务器上也搭建了MQ,建立了接收队列管理器,接收通道。具体步骤见本人之前的博客。地址:https://blog.csdn.net/qq_34569497/article/details/81197910

此处,模拟场景是,本地Windows机器放入测试消息后,在springboot项目中通过程序连接阿里云MQ并接收到消息。同一台机器同一个队列管理器上发送和接收消息,见本人之前的博客。地址:https://blog.csdn.net/qq_34569497/article/details/81184177。

Windows服务器上MQ发送消息,通过springboot接收阿里云服务器上消息。

由于此处只是测试通过程序接收消息,故代码只有接收部分。

1.使用java连接阿里云MQ接收消息

1.1 项目pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
 
	<groupId>com.ibmmq</groupId>
	<artifactId>ibmmq</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
 
	<name>ibmmq</name>
	<description>Demo project for Spring Boot</description>
 
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.3.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
 
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>
 
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
 
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jms</artifactId>
		</dependency>
 
		<dependency>
			<groupId>javax.jms</groupId>
			<artifactId>javax.jms-api</artifactId>
		</dependency>
 
		<dependency>
			<groupId>com.ibm.mq</groupId>
			<artifactId>com.ibm.mq.allclient</artifactId>
			<version>9.0.5.0</version>
		</dependency>
	</dependencies>
 
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
 
</project>

1.2 启动类IbmmqApplication.java

package com.ibmmq.ibmmq;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
 
@SpringBootApplication
@EnableTransactionManagement
public class IbmmqApplication {
 
	public static void main(String[] args) {
		SpringApplication.run(IbmmqApplication.class, args);
	}
}

1.3 IBM MQ 基本配置文件 application.properties

project.mq.host=120.79.239.214
project.mq.port=1416
project.mq.queue-manager=QM_RECEIVE
project.mq.channel=SEND_RECEIVE_CHL
#project.mq.channel=SYSTEM.DEF.SVRCONN 
#project.mq.channel=SEND_RECEIVE_CONN
project.mq.username=mqm
project.mq.password=lmf000
#project.mq.password=lmf888888
project.mq.receive-timeout=20000

#MQ安装服务器地址
project.mq.host=127.0.0.1
#端口
project.mq.port=1414
#(队列管理器名称)
project.mq.queue-manager=QM_APPLE
#(通道名称)
project.mq.channel=SYSTEM.DEF.SVRCONN
#创建的MQ用户
project.mq.username=mqm
#创建的MQ用户连接密码
project.mq.password=xxxxxx
project.mq.receive-timeout= 20000

1.4基本配置处理类JmsConfig.java 

package com.ibmmq.ibmmq.test;
 
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jms.connection.CachingConnectionFactory;
import org.springframework.jms.connection.JmsTransactionManager;
import org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter;
import org.springframework.jms.core.JmsOperations;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.transaction.PlatformTransactionManager;
 
import com.ibm.mq.jms.MQQueueConnectionFactory;
import com.ibm.msg.client.wmq.WMQConstants;
 
@Configuration
public class JmsConfig {   
    @Value("${project.mq.host}")
    private String host;
    @Value("${project.mq.port}")
    private Integer port;
    @Value("${project.mq.queue-manager}")
    private String queueManager;
    @Value("${project.mq.channel}")
    private String channel;
    @Value("${project.mq.username}")
    private String username;
    @Value("${project.mq.password}")
    private String password;
    @Value("${project.mq.receive-timeout}")
    private long receiveTimeout;
    @Bean
    public MQQueueConnectionFactory mqQueueConnectionFactory() {
        MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
        mqQueueConnectionFactory.setHostName(host);
        try {
        	mqQueueConnectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
            mqQueueConnectionFactory.setCCSID(1208);
            mqQueueConnectionFactory.setChannel(channel);
            mqQueueConnectionFactory.setPort(port);
            mqQueueConnectionFactory.setQueueManager(queueManager);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return mqQueueConnectionFactory;
    }
    @Bean
    UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter(MQQueueConnectionFactory mqQueueConnectionFactory) {
        UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter = new UserCredentialsConnectionFactoryAdapter();
            //用户认证需要没有可注释
        /*        userCredentialsConnectionFactoryAdapter.setUsername(username);
        userCredentialsConnectionFactoryAdapter.setPassword(password);*/
        userCredentialsConnectionFactoryAdapter.setTargetConnectionFactory(mqQueueConnectionFactory);
        return userCredentialsConnectionFactoryAdapter;
    }
    @Bean
    @Primary
    public CachingConnectionFactory cachingConnectionFactory(UserCredentialsConnectionFactoryAdapter userCredentialsConnectionFactoryAdapter) {
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setTargetConnectionFactory(userCredentialsConnectionFactoryAdapter);
        cachingConnectionFactory.setSessionCacheSize(500);
        cachingConnectionFactory.setReconnectOnException(true);
        return cachingConnectionFactory;
    }
    @Bean
    public PlatformTransactionManager jmsTransactionManager(CachingConnectionFactory cachingConnectionFactory) {
        JmsTransactionManager jmsTransactionManager = new JmsTransactionManager();
        jmsTransactionManager.setConnectionFactory(cachingConnectionFactory);
        return jmsTransactionManager;
    }
    @Bean
    public JmsOperations jmsOperations(CachingConnectionFactory cachingConnectionFactory) {
        JmsTemplate jmsTemplate = new JmsTemplate(cachingConnectionFactory);
        jmsTemplate.setReceiveTimeout(receiveTimeout);
        return jmsTemplate;
    }
}

1.5 接收消息类ReceiveMessage.java

(继承MessageListenerAdapter类且使用@JmsListener注解进行监听)

注意:此处若只使用@JmsListener注解监听,会出现监听消息获取不及时或者获取不到消息的情况,加上MessageListenerAdapter后便不会出现

package com.ibmmq.ibmmq.test;
 
import javax.jms.Message;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.core.JmsOperations;
import org.springframework.jms.listener.adapter.MessageListenerAdapter;
import org.springframework.stereotype.Component;
 
//消息消费者的类上必须加上@Component,或者是@Service,这样的话,消息消费者类就会被委派给Listener类,原理类似于使用SessionAwareMessageListener以及MessageListenerAdapter来实现消息驱动POJO
@Component
public class ReceiveMessage extends MessageListenerAdapter{
	@Autowired
	JmsOperations jmsOperations;
 
    @Override
    @JmsListener(destination = "RLQ")
    public void onMessage(Message message) {
        String messageBody = new String(message.toString());
    	System.out.println("成功监听Q1消息队列,传来的值为:" + messageBody);
    }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.core.JmsOperations;
import org.springframework.stereotype.Component;

//消息消费者的类上必须加上@Component,或者是@Service,这样的话,消息消费者类就会被委派给Listener类,原理类似于使用SessionAwareMessageListener以及MessageListenerAdapter来实现消息驱动POJO
@Component
public class ReceiveMessageText{
    @Autowired
    JmsOperations jmsOperations;

    @JmsListener(destination = "Q1")
    public void onMessage(String text) {
        System.out.println("成功监听Q1消息队列,传来的文本值为:" + text);
    }
}
成功监听Q1消息队列,传来的文本值为:my message 我的消息...

1.7 错误解决

在网上查找了很多资料,最后,有篇文章说将配置中的project.mq.channel=SEND_RECEIVE_CHL改为以下即可,

project.mq.channel=SYSTEM.DEF.SVRCONN

改完后,确实程序可以正常监听并接收消息。但是,根据文章https://blog.csdn.net/haitaofeiyang/article/details/46897323中将到的“注意:我们不建议用户连接SYSTEM.DEF.*通道。这些系统定义的通道是定义其它用户通道的模板。不建议用户使用SYSTEM.DEF.*和SYSTEM.AUTO.*通道。”。

那么,要如何使用自定义的通道呢。

所以就想到了对比自己建立的通道和系统自带的通道,发现:

系统自带:

dis chl(SYSTEM.DEF.SVRCONN)
     5 : dis chl(SYSTEM.DEF.SVRCONN)
AMQ8414: Display Channel details.
   CHANNEL(SYSTEM.DEF.SVRCONN)             CHLTYPE(SVRCONN)
   ALTDATE(2018-07-25)                     ALTTIME(09.41.50)
   CERTLABL( )                             COMPHDR(NONE)
   COMPMSG(NONE)                           DESCR( )
   DISCINT(0)                              HBINT(300)
   KAINT(AUTO)                             MAXINST(999999999)
   MAXINSTC(999999999)                     MAXMSGL(4194304)
   MCAUSER( )                              MONCHL(QMGR)
   RCVDATA( )                              RCVEXIT( )
   SCYDATA( )                              SCYEXIT( )
   SENDDATA( )                             SENDEXIT( )
   SHARECNV(10)                            SSLCAUTH(REQUIRED)
   SSLCIPH( )                              SSLPEER( )

自定义通道:

dis chl(SEND_RECEIVE_CHL)
     6 : dis chl(SEND_RECEIVE_CHL)
AMQ8414: Display Channel details.
   CHANNEL(SEND_RECEIVE_CHL)               CHLTYPE(RCVR)
   ALTDATE(2018-07-25)                     ALTTIME(09.51.03)
   BATCHSZ(50)                             CERTLABL( )
   COMPHDR(NONE)                           COMPMSG(NONE)
   DESCR( )                                HBINT(300)
   KAINT(AUTO)                             MAXMSGL(4194304)
   MCAUSER( )                              MONCHL(QMGR)
   MRDATA( )                               MREXIT( )
   MRRTY(10)                               MRTMR(1000)
   MSGDATA( )                              MSGEXIT( )
   NPMSPEED(FAST)                          PUTAUT(DEF)
   RCVDATA( )                              RCVEXIT( )
   RESETSEQ(NO)                            SCYDATA( )
   SCYEXIT( )                              SENDDATA( )
   SENDEXIT( )                             SEQWRAP(999999999)
   SSLCAUTH(REQUIRED)                      SSLCIPH( )
   SSLPEER( )                              STATCHL(QMGR)
   TRPTYPE(TCP)                            USEDLQ(YES)

对比可见,两者最大的不同即CHLTYPE,自定义的通道为RCVR,而系统自带的通道为SVRCONN。通过百度IBM MQ CHLTYPE类型,知道原来SVRCONN代表服务器连接通道,而RCVR代表接收方通道。

 服务器连接通道就是给MQ客户端连接进来的一个标识入口,它和其他通道不一样,它是不需要启动的,如果有MQ客户端成功地通过这个服务器连接通道连接进来,它的状态就是活动的了。因此我们需要在阿里云上对应的队列管理器中建立服务器连接通道后,才可通过java进行连接。

创建服务器连接通道:

DEFINE CHANNEL(SEND_RECEIVE_CONN) CHLTYPE(SVRCONN) TRPTYPE(TCP) 


要为受 SSL 保护的 IBM MessageSight 连接创建服务器连接通道:

DEFINE CHANNEL(SEND_RECEIVE_CONN) CHLTYPE(SVRCONN) TRPTYPE(TCP) SSLCIPH(cipherSpec)

创建好后,将java配置文件中channel改为:project.mq.channel=SEND_RECEIVE_CONN

再次启动服务,发现不再报错。

1.8 测试结果

本地远程中放入测试消息:

服务中监听到的消息:

 

成功监听Q1消息队列,传来的值为:
  JMSMessage class: jms_text
  JMSType:          null
  JMSDeliveryMode:  1
  JMSDeliveryDelay: 0
  JMSDeliveryTime:  0
  JMSExpiration:    0
  JMSPriority:      0
  JMSMessageID:     ID:414d5120514d5f53454e442020202020d71a595b24ad7a02
  JMSTimestamp:     1532596311700
  JMSCorrelationID: null
  JMSDestination:   null
  JMSReplyTo:       null
  JMSRedelivered:   false
    JMSXAppID: Windows\bin64\MQExplorer.exe
    JMSXDeliveryCount: 1
    JMSXUserID: lmf         
    JMS_IBM_Character_Set: UTF-8
    JMS_IBM_Encoding: 546
    JMS_IBM_Format: MQSTR   
    JMS_IBM_MsgType: 8
    JMS_IBM_PutApplType: 11
    JMS_IBM_PutDate: 20180726
    JMS_IBM_PutTime: 09115170
test my message

作者:1275012490 来源:CSDN 原文:https://blog.csdn.net/qq_34569497/article/details/81223958?utm_source=copy

package com.my.mqserver.mqserver.util;

import com.ibm.mq.*;

public class MQUtil {
    private String qManager;        // QueueManager名
    private MQQueueManager qMgr;    // 队列管理器名称
    private MQQueue qQueue;         // 消息通道
    String HOST_NAME;               // 主机名,在这里我填写了IP地址
    int PORT = 0;                   // 端口号
    String Q_NAME;                  // 本地队列
    String CHANNEL;                 // 连接通道
    int CCSID;
    String Msg;

    /**
     * 初始化
     */
    public void init() {
        try {
           /* HOST_NAME = "127.0.0.1";
            PORT = 1414;
            qManager = "QM_APPLE";
            Q_NAME = "Q1";
            CHANNEL = "QM_ORANGE.QM_APPLE";
            CCSID = 1383; // 表示是简体中文,
            MQEnvironment.hostname = HOST_NAME;
            MQEnvironment.port = PORT;
            MQEnvironment.channel = "SYSTEM.DEF.SVRCONN";    */
            HOST_NAME = "127.0.0.1";
            PORT = 1414;
            qManager = "QM_APPLE";
            Q_NAME = "Q1";
            CHANNEL = "QM_ORANGE.QM_APPLE";
            CCSID = 1383; // 表示是简体中文,
            MQEnvironment.hostname = HOST_NAME;
            MQEnvironment.port = PORT;
            MQEnvironment.channel = "SYSTEM.DEF.SVRCONN";
            MQEnvironment.CCSID = CCSID;
            qMgr = new MQQueueManager(qManager);
            int qOptioin = MQC.MQOO_INPUT_AS_Q_DEF | MQC.MQOO_INQUIRE | MQC.MQOO_OUTPUT;
            qQueue = qMgr.accessQueue(Q_NAME, qOptioin);
        } catch (MQException e) {
            e.printStackTrace();
            System.out.println("发生了一起异常,异常原因:" + e.reasonCode);
        }
    }

    void finalizer() {
        try {
            qQueue.close();
            qMgr.disconnect();
        } catch (MQException e) {
            System.out.println("发生了一起异常,异常原因:" + e.reasonCode);
        }
    }

    /*
     * 获取消息
     */
    public void GetMsg() throws ClassNotFoundException {
        try {
            MQMessage revMessage = new MQMessage();
            MQGetMessageOptions gmo = new MQGetMessageOptions();
            revMessage.characterSet=CCSID;
            revMessage.encoding=CCSID;
            gmo.options += MQC.MQPMO_SYNCPOINT;
            qQueue.get(revMessage, gmo);

            String revString = revMessage.readStringOfByteLength(revMessage.getMessageLength());
            System.out.println("接收到的内容:"+revString);

        } catch (RuntimeException e) {
            e.printStackTrace();
        } catch (MQException e) {
            if (e.reasonCode != 2033) // 没有消息
            {
                System.out.println("发生了一起异常,异常原因:" + e.reasonCode);
                e.printStackTrace();
            }
        } catch (java.io.IOException e) {
            System.out.println("发生了一起IO异常:" + e);
            e.printStackTrace();
        }
    }

    /**
     * 发送消息
     *
     */
    public void SendMsg(String msgStr) {
        try {
            MQMessage qMsg = new MQMessage();
            qMsg.encoding=CCSID;
            qMsg.characterSet=CCSID;
            qMsg.writeString(msgStr);
            MQPutMessageOptions pmo = new MQPutMessageOptions();
            qQueue.put(qMsg, pmo);
            System.out.println("消息发送成功!发送的内容是:" + msgStr);
        } catch (MQException e) {
            System.out.println("发生了一起异常,异常原因:" + e.reasonCode);
        } catch (java.io.IOException e) {
            System.out.println("发生了一起IO异常:" + e);
        }
    }

    public static void main(String[] args) {

        MQUtil mqst = new MQUtil();
        mqst.init();
        try {
            mqst.SendMsg("你好,我是一条测试消息!");
            //mqst.GetMsg();
        } catch (Exception e) {
            e.printStackTrace();
        }
        mqst.finalizer();
    }

}

 

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值