记一次SpringBoot项目IBM MQ接收处理速度变慢问题解决过程

记录这次IBM MQ消息队列接收变慢的发现和处理过程,告诫自己:码字需谨慎

先介绍我们遇到的问题:因为涉及公司业务,所以简单描述。就是利用springBoot开发一个接受IBM MQ消息队列的服务。消息对象主要是XML格式的报文,该服务主要就是从IBM MQ消息队列中获取XML格式字符型,然后获取XML报文中的某一个节点信息,作为文件名落地在一个固定的文件。

我们的SpringBoot项目是打包成jar,在Windows通过Java命令启动:

java -jar jar包名

一开始我们的接收速度还行,能接受,大概50毫秒接收一个IBMMQ消息落地成XML格式的报文。但是后来接收速度变慢,分析大概接收一个报文大概需要2000毫秒,我们的本地消息队列堵的消息数量越来越多,我们平台每天大概7万回执报文,队列很快堵到30万。接收回执报文速度慢,影响了我们对于报文内容的解析,影响业务运营。

根据运营同事描述,突然服务变慢了。因为我们的生产环境使用IBM MQ免费版,有三个月的试用期,每3个月都需要卸载IBM MQ软件重新安装,还好IBM MQ卸载会保留配置信息(消息队列、通道等等的配置信息),也还算简单。

因为中途我们重新安装过IBM MQ,所以运营同事给了我们第一次的误导:自从重新安装IBM MQ以后,接收报文越来越慢。我们的注意力全被转移到IMB MQ配置上面,猜测是不是因为IBM MQ试用版的原因。所有我们一直在纠结IBM MQ配置问题。

我观察Windows服务器的性能参数,发现服务已启动的时候内存爆满,CPU还行。我开始优化代码,减少String对象的时候,多用StringBuild和StringBuffer对象。后来,发现内存下降了一点。但是接受报文速度还是比较慢。

由于处理报文落地的代码不复杂,我们采取了最简单的办法:打印程序运行时间+二分查找法,通过两段代码运行时间对比,不断缩小范围,找到程序最耗时的代码范围:

打印程序运行时间

long startTime=System.currentTimeMillis();   //获取开始时间


程序代码

long endTime=System.currentTimeMillis(); //获取结束时间


System.out.println("程序运行时间: "+(end-start)+"ms");

最后终于定位到了耗时最长的一行代码,执行时间大概需要 1900毫秒

耗时代码:

//新建文件
if(!saveFile.exists()){
	saveFile.createNewFile();//创建文件耗时慢的一行代码
}

看到这一行代码,都惊呆了,为什么创建文件的代码会耗时这么长,一行代码。后来再仔细分析,终于找到原因了,超级坑爹,这是XML解析,获取节点信息时临时创建的一个文件,每次用完没有删除,导致临时文件里面的文件数量越来越多,大概180多万个文件存在,所以导致在文件夹下创建文件速度特别慢。

终于找到原因了,自己掉进了自己设置的陷阱进去,还是因为自己代码不够严谨。找到问题过程却让人有所收获。

 

代码优化

思考,解析XML的时候为什么需要创建这个临时文件呢,为什么不直接将接受字符进行解析呢?将字符串落地成临时报文需要读写磁盘,也是一个耗时过长,完全不必要的。于是修改了解析方法XML方法

 

原来:

/**
	 * 从传输报文中获取数据  BASE64解码
	 * 
	 * @param dataMap
	 * @param file
	 */
	public static Map<String,String> getXmlDate(String xml){
	    Map<String,String> retMap=new HashMap<String,String>();
	    Document document=null;
		try{
			//得到document
			document=DocumentHelper.parseText(xml);
			//得到第一个根节点
			Element root = document.getRootElement();
			Element msgIdEl = root.element("DxpMsg").element("Data");
			String data=msgIdEl.getText();
			//base64解密
			String ret= Encodes.decodeBase64String(data);
			retMap.put("Data", ret);
			Element msgTypeEl = root.element("DxpMsg").element("TransInfo").element("MsgType");
			String msgType=msgTypeEl.getText();
			retMap.put("MsgType", msgType);
		}catch(Exception e){
			log.error("从传输报文获取数据异常:"+e);
		}
		return retMap;
	}

改写后:

/**
	 * 从传输报文中获取数据  BASE64解码
	 * 
	 * @param dataMap
	 * @param file
	 */
	public static Map<String,String> getXmlDate(File file){
	    Map<String,String> retMap=new HashMap<String,String>();
		try{
			//得到document
			Document document =getDocument(file.getAbsolutePath());
			//得到第一个根节点
			Element root = document.getRootElement();
			Element msgIdEl = root.element("DxpMsg").element("Data");
			String data=msgIdEl.getText();
			//base64解密
			String ret= Encodes.decodeBase64String(data);
			retMap.put("Data", ret);
			Element msgTypeEl = root.element("DxpMsg").element("TransInfo").element("MsgType");
			String msgType=msgTypeEl.getText();
			retMap.put("MsgType", msgType);
		}catch(Exception e){
			log.error("从传输报文获取数据异常:"+e);
		}
		return retMap;
	}

两个方法区别就是参数不一样,一个接受文件,一个直接传入字符串。这样就不需要临时创建临时文件,直接解析报文内容,减少了不必要的读写磁盘时间。

主要是记录这次解决问题的方式:

1、二分排除法+打印代码执行时间=定位耗时的代码位置。

2、Windows系统一个文件夹下文件数量有上限,达到一定数量,创建新文件的耗时就会增加。

 

以下是SpringBoot整合IBM MQ的示例代码: 1. 创建一个Spring Boot项目,并添加所需的依赖。 ```xml <dependency> <groupId>com.ibm.mq</groupId> <artifactId>mq-jms-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> ``` 2. 在application.properties文件中配置IBM MQ连接信息。 ```properties ibm.mq.queueManager=QUEUE_MANAGER_NAME ibm.mq.channel=CHANNEL_NAME ibm.mq.connName=HOST(IP_ADDRESS)(PORT) ibm.mq.user=USERNAME ibm.mq.password=PASSWORD ``` 3. 创建一个消息发送者。 ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.core.JmsTemplate; import org.springframework.stereotype.Component; @Component public class MessageSender { @Autowired private JmsTemplate jmsTemplate; public void sendMessage(String queueName, String message) { jmsTemplate.convertAndSend(queueName, message); } } ``` 4. 创建一个消息接收者。 ```java import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Component; @Component public class MessageReceiver { @JmsListener(destination = "QUEUE_NAME") public void receiveMessage(String message) { System.out.println("Received message: " + message); } } ``` 5. 在启动类中添加@EnableJms注解。 ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.jms.annotation.EnableJms; @SpringBootApplication @EnableJms public class IbmmqApplication { public static void main(String[] args) { SpringApplication.run(IbmmqApplication.class, args); } } ``` 这样就完成了Spring Boot与IBM MQ整合。你可以使用MessageSender发送消息到指定的队列,然后MessageReceiver会接收并处理这些消息。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值