spring+activemq 发送10W消息报端口被占用的异常分析以及topic持久化订阅

来源:互联网 发布:淘宝男装店铺排行粉丝 编辑:程序博客网 时间:2024/06/01 12:36

转载来自:http://greemranqq.iteye.com/blog/2167158


、问题解析: 

       1.测试 发送10W消息,中途会出现

         socket: tcp://localhost:61616 due to: java.net.BindException: Address already in use: JVM_Bind 异常。

         你关掉activemq,利用netstat -aon | findstr "61616"   发现没有这个端口占用情况,查阅资料才知道:

         http://activemq.apache.org/jmstemplate-gotchas.html

         里面解释道,发送消息的时候创建connection,session 还要关闭,比较费资源,我猜测当创建销毁操作没测试完成的时候,另一个消息发送的时候,发现端口被占着,就会出现这个种情况,也就是说当发送频率比较高的情况,容易出现,文档建议用 pool 的东西。

        一共有spring 的CachingConnectionFactory 和 activemq 的PooledConnectionFactory,由于PooledConnectionFactory 这东西要 activemq-pool.jar ,因此我还是选择CachingConnectionFactory~。~。

       配置spring-jms.xml更改如下:

       

Java代码  收藏代码
  1. <!-- jms 连接工厂 -->  
  2.   <bean id="connectionFactory" class="org.apache.activemq.spring.ActiveMQConnectionFactory">  
  3.       <property name="brokerURL" value="tcp://localhost:61616?jms.useAsyncSend=false" />  
  4.   </bean>  
  5.   
  6.   <bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">  
  7.       <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
  8.       <property name="targetConnectionFactory" ref="connectionFactory"/>  
  9.       <!-- Session缓存数量,这里属性也可以直接在这里配置 -->  
  10.       <property name="sessionCacheSize" value="100" />  
  11.   
  12. <!-- 基本的bean模板 -->  
  13.   <bean id = "jmsTemplate" class = "org.springframework.jms.core.JmsTemplate">  
  14.       <!-- 链接工厂,这里应用缓存池 就行了-->  
  15.       <property name="connectionFactory" ref="cachingConnectionFactory"/>  
  16.   </bean>  
  17.   </bean>  

  

   这样操作了时候,发送20W条信息也没出现过问题,而且速度比发送快了几十倍......

 

   2.topic 模式下如果A 消费者挂了,就收不到消息了,而我又想它收到消息,我们先来尝试持久化吧!

      1.对于持久化,并不默认,其实queue 默认就持久化在文件里面的,但是topic 模式下我们得开启持久化配置, 在activemq.xml 里面有这样的配置:

     

Java代码  收藏代码
  1. <!-- 这里设置true 就算开启了 -->  
  2. <broker xmlns="http://activemq.apache.org/schema/core" persistent="true"   brokerName="localhost" dataDirectory="${activemq.base}/data">  
  3.   
  4. <!-- 这里是文件存放的位置,其他的说明暂时不讲 -->  
  5.       <persistenceAdapter>  
  6.             <amqPersistenceAdapter  syncOnWrite="true"  directory="${activemq.base}/data2" maxFileLength="3mb"/>  
  7.         </persistenceAdapter>  

   

 

     2.持久化我们在spring-jms.xml 里面还得开启几个东西:

     

Java代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.        xsi:schemaLocation="  
  5.         http://www.springframework.org/schema/beans  
  6.         http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">  
  7.   
  8.     <!-- jms 连接工厂 -->  
  9.     <bean id="connectionFactory" class="org.apache.activemq.spring.ActiveMQConnectionFactory">  
  10.         <property name="brokerURL" value="tcp://localhost:61616?jms.useAsyncSend=true" />  
  11.     </bean>  
  12.   
  13.     <bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">  
  14.         <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
  15.         <property name="targetConnectionFactory" ref="connectionFactory"/>  
  16.         <!-- Session缓存数量 -->  
  17.         <property name="sessionCacheSize" value="100" />  
  18.     </bean>  
  19.   
  20.     <!-- 基本的bean模板 -->  
  21.     <bean id = "jmsTemplate" class = "org.springframework.jms.core.JmsTemplate">  
  22.         <!-- 链接工长 -->  
  23.         <property name="connectionFactory" ref="cachingConnectionFactory"/>  
  24.         <!-- 进行持久化 -->  
  25.         <property name="deliveryMode" value="2" />  
  26.          <!--订阅 发布模式 -->  
  27.         <property name="pubSubDomain" value="true" />  
  28.     </bean>  
  29.     <!-- 消息订阅模式 -->  
  30.     <bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">  
  31.         <!-- 订阅消息的名字 -->  
  32.         <constructor-arg index="0" value="orderTopic"/>  
  33.     </bean>  
  34.   
  35. </beans>  

 

 

   3.想想我们订阅者需要做些什么呢?发布者发布消息,订阅者去消费,这是1对多的形式,我们可以这样理解:公司设定很多活动代金卷,去参加活动的人都能领取,当然这分两种情况,第一种 就是我们前面测试的,只要我公司门口等(监听),活动开始(发布)就能领取了,如果你当时没在,就领取不到。第二种:很多情况下,公司搞活动我们不会等在那里,只要活动开始了,那么我过段时间也可以去,礼品公司会保留的,这种情况会导致多次领取,因此总要登记一下嘛,不能你领取了,过一会又来吧?activemq 里面会有clientId 标示来区分,类似于身份证ID嘛。

      当然有些情况下, 我们一个ID 可以领取多个不同的奖品,因此还得需要个字段标示:durableSubscriptionName,标示我们领取哪个礼品,下面先看配置

    

Java代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.        xsi:schemaLocation="  
  5.         http://www.springframework.org/schema/beans  
  6.         http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">  
  7.   
  8.     <!-- jms 连接工厂 -->  
  9.     <bean id="connectionFactory" class="org.apache.activemq.spring.ActiveMQConnectionFactory">  
  10.          
  11.         <property name="brokerURL" value="tcp://localhost:61616?jms.useAsyncSend=true" />  
  12.     </bean>  
  13.   
  14.     <bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">  
  15.         <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
  16.         <property name="targetConnectionFactory" ref="connectionFactory"/>  
  17.         <!-- 接收者ID -->  
  18.         <property name="clientId" value="clientA" />  
  19.     </bean>  
  20.     <!-- 消息订阅模式 -->  
  21.     <bean id="topicCustomerA" class="org.apache.activemq.command.ActiveMQTopic">  
  22.         <!-- 订阅消息的名字 -->  
  23.         <constructor-arg index="0" value="orderTopic"/>  
  24.     </bean>  
  25.   
  26.     <!-- 消息监听,这里可以认为是A服务器的监听 -->  
  27.     <bean id="messageListener" class="com.raycloud.excalibur.mq.ConsumerMessageListener"/>  
  28.     <bean id="listenerContainerA" class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
  29.         <property name="connectionFactory" ref="connectionFactory" />  
  30.         <property name="destination" ref="topicCustomerA" />  
  31.         <property name="messageListener" ref="messageListener" />  
  32.         <!-- 持久化消息 -->  
  33.         <property name="subscriptionDurable" value="true"/>  
  34.         <!-- 接收者ID -->  
  35.         <property name="clientId" value="clientA" />  
  36.         <!-- 这里名字可以任意改变,A 领取了,你可以改成B 还可以领取,可以举例不是很恰当 -->  
  37.         <property name="durableSubscriptionName" value="clientA"/>  
  38.     </bean>  
  39.   
  40. </beans>  

     

Java代码  收藏代码
  1. // 这是消费者代码,这里你可以创建 多个XMl文件,模拟多个消费者。  
  2. public class JmsTopicReceiver{  
  3.     public static void main(String[] args) throws Exception {  
  4.         // 加载消费者监听  
  5.         ApplicationContext context = new ClassPathXmlApplicationContext("spring/spring-jms-consumerA.xml");  
  6.         // 写个死循环,模拟服务器一直运行  
  7.         while (true){}  
  8.     }  
  9. }  

 

 

     

Java代码  收藏代码
  1. // 监听代码 直接输出  
  2. public class ConsumerMessageListener implements MessageListener {  
  3.     @Override  
  4.     public void onMessage(Message message) {  
  5.         System.out.println("topic 收到消息:"+message);  
  6.     }  
  7. }  

  

 

三、测试:

       我们要完成一个收到一个order,然后N个服务计算的问题,因此采用topic 模式,而防止中途服务器挂掉,采用持久化方式,模拟测试如下:

       1.启动两个ConsumerA,ConsumerB 监听,发布一个order ,同时收到消息,OK

       2.启动一个ConsumerA,发布一个Order,再启动ConsumerB,也收到消息,OK

       3.启动一个ConsumerA,发布一个Order,A收到,关闭mq服务器,重启mq服务(前后),重启ConsumerB ,同样收到消息,OK。

       4.启动两个ConsumerA,ConsumerB,发布order,A,B收到消息,重启A,B  不收重复消息,OK   

       好像基本能满足需求了,由于发送量 不会很大,频率不会很高,可以试用一下了。

 

      那么新问题是:

      1.如果A,B 收到消息后,topic  的消息怎么处理呢? 一直保存着吗? 如果可以清除,怎么清除,什么时候进行清除呢?文件的方式方便管理吗?

      2.虽然消息发送过去了,对象信息怎么接受呢,当然会有消息转换器..

      3.在queue 模式下,服务器断开了,怎么从新连接呢,如果服务器挂了,怎么切换到备用的呢?

 

小结:

        1.这是初步尝试了下 topic 持久化到文件,当然也可以持久化到数据的,关于activemq 持久化以及介绍文章,可以参考:http://blog.csdn.net/xyw_blog/article/details/9128219 比较详细。

        2.


0 0