ActiveMQ编程实例

    本文主要展示如何使用activeMQ进行程序设计,可以作为代码实例参考;此后会继续补充有关JMS 或ActiveMQ的优化和架构部分。

    本实例主要展示如何使用Queue。

 

一.pom.xml

 

Java代码   收藏代码
  1. <dependencies>  
  2.         <dependency>  
  3.         <groupId>org.springframework</groupId>  
  4.         <artifactId>spring-context</artifactId>  
  5.         <version>3.2.3.RELEASE</version>  
  6.     </dependency>  
  7.     <dependency>  
  8.         <groupId>org.springframework</groupId>  
  9.         <artifactId>spring-jms</artifactId>  
  10.         <version>3.2.3.RELEASE</version>  
  11.     </dependency>  
  12.     <dependency>  
  13.         <groupId>commons-lang</groupId>  
  14.         <artifactId>commons-lang</artifactId>  
  15.         <version>2.4</version>  
  16.     </dependency>  
  17.     <dependency>  
  18.         <groupId>javax.jms</groupId>  
  19.         <artifactId>jms</artifactId>  
  20.         <version>1.1</version>  
  21.     </dependency>  
  22.     <dependency>  
  23.         <groupId>org.apache.qpid</groupId>  
  24.         <artifactId>proton-jms</artifactId>  
  25.         <version>0.3</version>  
  26.     </dependency>  
  27.              
  28.     <dependency>  
  29.         <groupId>org.apache.activemq</groupId>  
  30.         <artifactId>activemq-all</artifactId>  
  31.         <version>5.8.0</version>  
  32.     </dependency>  
  33. </dependencies>  
  34. <build>  
  35.     <finalName>test-jms-1.0</finalName>  
  36.     <resources>  
  37.         <resource>  
  38.         <directory>src/main/resources</directory>  
  39.         <filtering>true</filtering>  
  40.         </resource>  
  41.     </resources>  
  42. </build>  
    其中“proton-jms”是需要声明的,否则可能无法正常运行。

 

 

二.Java实例(非spring环境)

    对JMS的程序部分,推荐使用JNDI + 异步listener方式;所以接下来的例子将采取此方式。

    1) jndi.properties----[/src/main/resources/jndi.properties]

 

Java代码   收藏代码
  1. ###contextFactory  
  2. java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory  
  3. ###brokerUrl,any protocol  
  4. java.naming.provider.url = tcp://localhost:61616  
  5. ##username  
  6. ##java.naming.security.principal=  
  7. ##password  
  8. ##java.naming.security.credentials=  
  9. ##connectionFactory,for building sessions  
  10. connectionFactoryNames = QueueCF,TopicCF  
  11. ##topic.<topicName> = <physicalName-of-topic>  
  12. ##your application should use <topicName>,such as:  
  13. ## context.lookup("topic1");  
  14. ##It can be more than once  
  15. topic.topic1 = jms.topic1  
  16. ##queue.<topicName> = <physicalName-of-queue>  
  17. queue.queue1 = jms.queue1  
   
    2) QueueMessageListener.java(消息异步监听器)

 

Java代码   收藏代码
  1. public class QueueMessageListener implements MessageListener{  
  2.   
  3.     public void onMessage(Message message) {  
  4.         if(message == null){  
  5.             return;  
  6.         }  
  7.         try{  
  8.             if(message instanceof TextMessage){  
  9.                 String text = ((TextMessage)message).getText();  
  10.                 System.out.println("-----JMS Message header-----");  
  11.                 //message的关联id,可以在发送消息时指定,用于描述消息的关联性  
  12.                 System.out.println("CorrelationID :" + message.getJMSCorrelationID());  
  13.                 //消息的“传送模式”,1:非持久,2:持久  
  14.                 System.out.println("DeliveryMode :" + message.getJMSDeliveryMode());  
  15.                 //消息的过期时间,毫秒数;如果在发送消息时指定了timeToLive,此值为timestap + timeToLive  
  16.                 System.out.println("Expiration :" + message.getJMSExpiration());  
  17.                 //消息ID,全局唯一  
  18.                 System.out.println("MessageID :" + message.getJMSMessageID());  
  19.                 //消息权重,参考属性  
  20.                 System.out.println("Priority :" + message.getJMSPriority());  
  21.                 //是否为“重发”;当一个消息发送给消费者之后,未能收到“确认”,将会导致消息重发  
  22.                 System.out.println("Redelivered :" +message.getJMSRedelivered());  
  23.                 //消息创建的时间戳,当消息发送时被赋值。  
  24.                 System.out.println("Timestamp :" + message.getJMSTimestamp());  
  25.                 //消息的类型  
  26.                 System.out.println("Type :" + message.getJMSType());  
  27.                 System.out.println("-----Message Properties-----");  
  28.                 Enumeration<String> names = message.getPropertyNames();  
  29.                 if(names != null){  
  30.                     while(names.hasMoreElements()){  
  31.                         String key = names.nextElement();  
  32.                         System.out.println(key + ":" + message.getStringProperty(key));  
  33.                     }  
  34.                 }  
  35.                 System.out.println(">>>>" + text);  
  36.             }  
  37.         }catch(Exception e){  
  38.             e.printStackTrace();  
  39.         }  
  40.     }  
  41. }  
  
    3) SimpleConsumer.java(消息消费者)

     因为我们已经使用了“MessageListener”来异步接受消息,事实上JMS的实现中已经开启了单独的线程用来从网络中接受消息,并逐次调用onMessage方法;此处我们没有必要再次额外的创建线程。

 

Java代码   收藏代码
  1. public class SimpleConsumer {  
  2.   
  3.     private Connection connection;  
  4.     private Session session;  
  5.     private MessageConsumer consumer;  
  6.       
  7.     private boolean isStarted;  
  8.       
  9.     public SimpleConsumer(MessageListener listener) throws Exception{  
  10.         Context context = new InitialContext();  
  11.         ConnectionFactory connectionFactory = (QueueConnectionFactory)context.lookup("QueueCF");  
  12.         connection = connectionFactory.createConnection();  
  13.         session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);  
  14.         Destination queue = (Queue)context.lookup("queue1");  
  15.         consumer = session.createConsumer(queue);  
  16.         consumer.setMessageListener(listener);  
  17.     }  
  18.       
  19.       
  20.     public synchronized boolean start(){  
  21.         if(isStarted){  
  22.             return true;  
  23.         }  
  24.         try{  
  25.             connection.start();//very important  
  26.             isStarted = true;  
  27.             return true;  
  28.         }catch(Exception e){  
  29.             return false;  
  30.         }  
  31.     }  
  32.       
  33.     public synchronized void close(){  
  34.         isStarted = false;  
  35.         try{  
  36.             session.close();  
  37.             connection.close();  
  38.         }catch(Exception e){  
  39.             //  
  40.         }  
  41.     }  
  42. }  
  
    4) SimpleProductor.java(消息生产者)

 

Java代码   收藏代码
  1. public class SimpleProductor {  
  2.   
  3.     private MessageProducer producer;  
  4.     private Session session;  
  5.     private Connection connection;  
  6.     private boolean isOpen = true;  
  7.       
  8.     public SimpleProductor() throws Exception{  
  9.         Context context = new InitialContext();  
  10.         ConnectionFactory connectionFactory = (QueueConnectionFactory)context.lookup("QueueCF");  
  11.         connection = connectionFactory.createConnection();  
  12.         session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);  
  13.         Destination queue = (Queue)context.lookup("queue1");  
  14.         producer = session.createProducer(queue);  
  15.         producer.setDeliveryMode(DeliveryMode.PERSISTENT);  
  16.           
  17.     }  
  18.       
  19.       
  20.     public boolean send(String message) {  
  21.         if(!isOpen){  
  22.             throw new RuntimeException("session has been closed!");  
  23.         }  
  24.         try{  
  25.             producer.send(session.createTextMessage(message));  
  26.             return true;  
  27.         }catch(Exception e){  
  28.             return false;  
  29.         }  
  30.     }  
  31.       
  32.     public synchronized void close(){  
  33.         try{  
  34.             if(isOpen){  
  35.                 isOpen = false;  
  36.             }  
  37.             session.close();  
  38.             connection.close();  
  39.         }catch (Exception e) {  
  40.             //  
  41.         }  
  42.     }  
  43.       
  44. }  

 

    上面的程序,基本上可以完成简单的消息发送和接受,此外,还有一种不常用的方式:

Java代码   收藏代码
  1. ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");  
  2. Connection connection = connectionFactory.createConnection();  
  3. Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);  
  4. Destination queue = session.createQueue(QUEUE);  
  5. MessageProducer producer = session.createProducer(queue);  
  6. //MessageConsumer consumer = session.createConsumer(queue)  
Java代码   收藏代码
  1. //同步接收消息  
  2. MessageConsumer consumer = session.createConsumer(queue);  
  3. connection.start();  
  4. while(true){  
  5.     Message message = consumer.receive(10000);  
  6.     if(message == null){  
  7.         continue;  
  8.     }  
  9.     if(message instanceof TextMessage){  
  10.         //...  
  11.     }  
  12. }  

 

    5) 测试方法:

Java代码   收藏代码
  1. SimpleProductor productor = new SimpleProductor();  
  2. productor.start();  
  3. for(int i=0; i<10; i++){  
  4.     productor.send("message content:" + i);  
  5. }  
  6. productor.close();  
  7. SimpleConsumer consumer = new SimpleConsumer(new QueueMessageListener());  
  8. consumer.start();  
  9.   
  10. //consumer.close();  

 

三.Spring-jms实例

 

    1) 配置文件:

Java代码   收藏代码
  1. <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
  2.     <property name="brokerURL" value="tcp://localhost:61616"></property>  
  3. </bean>  
  4. <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">  
  5.     <constructor-arg index="0" value="queue1"></constructor-arg>  
  6. </bean>  
  7. <!-- productor -->  
  8. <bean id="queueTemplate" class="org.springframework.jms.core.JmsTemplate">  
  9.     <property name="connectionFactory" ref="connectionFactory"></property>  
  10.     <property name="defaultDestination" ref="queueDestination"></property>  
  11.     <!-- 是否在message中开启timestamp属性 -->  
  12.     <property name="messageTimestampEnabled" value="true"></property>  
  13.     <!-- 是否开启deliveryMode,priority,timeToLive消息附属属性 ,否则上述3个属性将采用默认值-->  
  14.     <property name="explicitQosEnabled" value="true"></property>  
  15.     <!-- NON_PERSISTENT = 1,PERSISTENT = 2,默认值为2-->  
  16.     <property name="deliveryMode" value="2"></property>  
  17.     <!-- pubSubNoLocal,对于topic而言,还需要注意此选项:是否接受本地消息,当消费者和生产者公用一个connection时 -->  
  18. </bean>  
  19. <bean id="productor" class="com.test.jms.spring.impl.ProductorImpl">  
  20.     <property name="jmsTemplate" ref="queueTemplate"></property>  
  21. </bean>  
  22. <!-- MDB -->  
  23. <bean id="queueMessageListener" class="com.test.jms.object.QueueMessageListener"/>  
  24. <bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
  25.     <property name="connectionFactory" ref="connectionFactory"></property>  
  26.     <property name="destination" ref="queueDestination"></property>  
  27.     <property name="messageListener" ref="queueMessageListener"></property>  
  28.     <!-- 如果消息的接收速率,大于消息处理的速率时,可以采取线程池方式   
  29.     <property name="taskExecutor" ref="queueMessageExecutor"></property>  
  30.     -->  
  31.      <!--  -->  
  32.     <property name="concurrentConsumers" value="1"></property>  
  33.     <!-- [concurrentConsumers]-[maxConcurrentConsumers] -->  
  34.     <!--    
  35.     <property name="concurrency" value="1-5"></property>  
  36.     -->  
  37.       
  38. </bean>  
  39. <bean id="queueMessageExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">  
  40.     <property name="corePoolSize" value="2" />  
  41.     <property name="maxPoolSize" value="5" />  
  42.     <property name="daemon" value="true" />  
  43.     <property name="keepAliveSeconds" value="120" />  
  44. </bean>  

    我们采用了spring推荐的方式:消息生产者基于jmsTemplate,消息消费者基于MDB(pojo的消息驱动bean);为了提升消息的接收者的吞吐能力,我们可以采取多线程的方式,其中有几个重要的参考配置:

    A) taskExecutor:消息接受端使用线程池的方式处理消息;当消息的网络输入速率大于消息的处理速率时,可以考虑采用此方式;在消息消费者的与JMS-Server的网络链接中,每收到一条消息,将会立即交付给线程池中的线程去执行,执行时仍然调用messageListener的方法;此处需要注意,线程池中的所有线程仍然共享一个messageListener实例,在采用线程池模式中,请注意线程安全问题

    B) concurrentConsumers:并发运行的消费者个数,在默认情况下为一个“消息消费者”;事实上,一个consumer即为一个Session,多个consumer即为多个Session;不过它们底层仍然共享一个“tcp链接”;此配置项仍然是适用于“消息的网络输入速率大于消息的处理速率”的场景;每一个consumer都将会在单独的线程中运行,但是它们仍然共享一个messageListener实例;在此场景下,你无法绝对的保证,原本“有序”的消息在交付给多个consumer时被实际执行的顺序也是严格的。

    taskExecutor是一种额外的优化策略,concurrentConsumers则是采用了JMS原生的特性;在实际场景中,我们选择一种即可。如下为Spring-JMS是如何使用线程池处理消息的原理(基于封装的思想):

Java代码   收藏代码
  1. if (this.taskExecutor != null) {  
  2.     consumer.setMessageListener(new MessageListener() {  
  3.         public void onMessage(final Message message) {  
  4.             taskExecutor.execute(new Runnable() {  
  5.                 public void run() {  
  6.                     processMessage(message, session);  
  7.                 }  
  8.             });  
  9.         }  
  10.     });  
  11. }  
  12. else {  
  13.     consumer.setMessageListener(new MessageListener() {  
  14.         public void onMessage(Message message) {  
  15.             processMessage(message, session);  
  16.         }  
  17.     });  
  18. }  

 

    2) ProductorImpl.java

Java代码   收藏代码
  1. public class ProductorImpl implements Productor {  
  2.   
  3.     private JmsTemplate jmsTemplate;  
  4.       
  5.     public void setJmsTemplate(JmsTemplate jmsTemplate) {  
  6.         this.jmsTemplate = jmsTemplate;  
  7.     }  
  8.   
  9.     public void send(final String message) {  
  10.         if(message == null){  
  11.             return;  
  12.         }  
  13.         jmsTemplate.send(new MessageCreator() {  
  14.             public Message createMessage(Session session) throws JMSException {  
  15.                 return session.createTextMessage(message);  
  16.             }  
  17.         });  
  18.     }  
  19.   
  20.     public void send(final Map<String, String> message) {  
  21.         if(message == null || message.isEmpty()){  
  22.             return;  
  23.         }  
  24.         jmsTemplate.send(new MessageCreator() {  
  25.             public Message createMessage(Session session) throws JMSException {  
  26.                 MapMessage mm = session.createMapMessage();  
  27.                 for(Entry<String, String> entry : message.entrySet()){  
  28.                     mm.setString(entry.getKey(), entry.getValue());  
  29.                 }  
  30.                 return mm;  
  31.             }  
  32.         });  
  33.           
  34.     }  
  35.   
  36.       
  37. }  

    非常的简单,我们不用书写和consumer有关的代码。一切就结束了。[备注,本文中的实例命名为Productor,只是为了避免和JMS中producer混淆]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值