简单jms连接池和一点使用心得

给你一囗甜甜゛ 提交于 2019-12-03 05:00:53

    jms在分布式应用中使用的非常多,在应用中加入一个简单的消息队列,即可达到解藕和高性能。比如是一个系统处理请求并把数据存入数据库,但中间加入消息队列后,发送和消费消息的项目可分开部署多个,加入lvs更可达到高可用,下面看一个我项目中的部署结构:

                            图:apollo部署结构和各实例部署情况

使用jms需要注意的问题:

1). 不要频繁的建立和关闭连接

JMS使用长连接方式,一个程序,只要和JMS服务器保持一个连接就可以了,不要频繁的建立和关闭连接。频繁的建立和关闭连接,对程序的性能影响还是很大的。这一点和jdbc还是不太一样的。

2). Connection的start()和stop()方法代价很高

JMS 的Connection的start()和stop()方法代价很高,不能经常调用。我们试用的时候,写了个jms的connection pool,每次将connection取出pool时调用start()方法,归还时调用stop()方法,然而后来用jprofiler发现,一般的 cpu时间都耗在了这两个方法上。

3). start()后才能收消息

Connection的start()方法调用后,才能收到jms消息。如果不调用这个方法,能发出消息,但是一直收不到消息。不知道其它的jms服务器也是这样。

4). 显示关闭Session

如果忘记了最后关闭Connection或Session对象,都会导致内存泄漏。这个在我测试的时候也发现了。本来以为关闭了Connection,由这 个Connection生成的Session也会被自动关闭,结果并非如此,Session并没有关闭,导致内存泄漏。所以一定要显示的关闭 Connection和Session。

5). 对Session做对象池

对Session做对象池,而不是 Connection。Session也是昂贵的对象,每次使用都新建和关闭,代价也非常高。而且后来我们发现,原来Connection是线程安全的, 而Session不是,所以后来改成了对Session做对象池,而只保留一个Connection。

    针对这些建议,有一些比较好的实践:

1、对于发送消息,可以使用spring提供的jmsTemplate,但是会发生spring的jmsTemplate与activemq的prefetch机制配合导致的问题,原问题链接:

http://www.cnblogs.com/baibaluo/archive/2012/12/24/2748468.html

那么建议使用连接池来发送消息:

<!--创建连接工厂-->
<bean id="innerConnectionFactory"  class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL" value="${brokerURL}"/>
        <property name="userName" value="${userName}"/>
	<property name="password" value="${password}"/>
    <property name="maxThreadPoolSize" value="100"/>
</bean>

<!-- The PooledConnectionFactory supports the pooling of Connection, Session and MessageProducer instances  so it can be used with tools like Camel and Spring's JmsTemplate and MessagListenerContainer . Connections, sessions and producers are returned to a pool after use so that they can be reused later without having to undergo the cost of creating them again. -->    
<bean id="connectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
    <property name="connectionFactory" ref="innerConnectionFactory" />
</bean>

<!-- 声明ActiveMQ消息目标,目标可以是一个队列,也可以是一个主题ActiveMQTopic-->
<bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">
    <constructor-arg index="0" value="${destination}"></constructor-arg>
</bean>

<!-- JMSTemplate -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="explicitQosEnabled" value="${explicitQosEnabled}" />
    <!-- 1:Non-Persistent,2:PERSISTENT -->
    <property name="deliveryMode" value="${deliveryMode}" />
    <property name="timeToLive" value="${timeToLive}"/> 
    <property name="defaultDestination" ref="destination" />
    <property name="receiveTimeout" value="${receiveTimeout}" />
</bean>

那么在发送时可以如此发送就可以直接使用PooledConnectionFactory,每次使用的connection也不会关闭,并且还可以使用prefeche机制。在使用时可以这样使用:

@ResponseBody
@RequestMapping(value = "/pay/{1}/{2}/{3}", method = RequestMethod.GET)
public void receiverTest(final HttpServletRequest request,	HttpServletResponse response) {
	// 使用JMSTemplate异步发送访问数据
	jmsTemplatePays.send(new MessageCreator() {
		public Message createMessage(Session session) throws JMSException {
			Map<String, Object> paramMap = new HashMap<String, Object>();
			MapMessage message = session.createMapMessage();
			paramMap.put("key0", "value0");
			message.setBytes("appPayRecords",SerializeUtil.serialize(paramMap));
			return message;
		}
	});
}

2、对于消费消息,建议是使用MessageListener机制,而不用receive方法,原因是receive方法会阻塞,那么在多个消息队列时,会阻塞于某一个没有消息的队列,而没有去消费有消息的队列。可以像如下方法来使用messageListener:

ConsumerTool cc = new ConsumerTool();
cc.startConsumeMessage();

但是需要自己加入处理消息体的代码,即处理消息的逻辑代码。

具体可查看我写的一个jms连接池,https://github.com/wuskyfantasy/apollo.jmspool

而如果使用JmsUtil.publish或JmsUtil.receive(等待时间),不仅可以达到向多个消息队列发送和获取消息,还可以达到断开的链接自动重连,可查看JmsUtil

    



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!