jms activeMQ与spring集成进阶篇

来源:互联网 发布:京东内部下单软件 编辑:程序博客网 时间:2024/05/01 04:32

前不久,刚学习了jms的简单入门,后面紧接着就做了一个关于jms的负载均衡的项目,做完之后颇有打通任督二脉的感觉,感觉很多之前不是很理解的东西,都有些理解了,比如服务器端的监听、具体的jms的使用等,收获有点大。

流程如下图所示:

客户端:

xml配置,这里用到了两台服务器,connectionFactory便可以看出,因为传的是对象,用到了转换器

复制代码
 1 <?xml version="1.0" encoding="UTF-8"?> 2  3 <beans xmlns="http://www.springframework.org/schema/beans" 4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5     xmlns:context="http://www.springframework.org/schema/context" 6     xsi:schemaLocation="http://www.springframework.org/schema/beans   7         http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   8         http://www.springframework.org/schema/context   9         http://www.springframework.org/schema/context/spring-context-2.5.xsd">10 11 12     <!-- 配置JMS连接工厂 -->13 14 <!--    <bean id="connectionFactory" class="org.apache.activemq.spring.ActiveMQConnectionFactory">-->15 <!--        <property name="brokerURL" value="tcp://localhost:61616" />-->16 <!--    </bean>-->17 18     <bean id="connectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">19         <property name="connectionFactory">20             <bean class="org.apache.activemq.ActiveMQConnectionFactory">21                 <property name="brokerURL">22                     <value>tcp://localhost:61616</value>23                 </property>24                 <property name="useAsyncSend">25                     <value>true</value>26                 </property>27             </bean>28         </property>29     </bean>30 31 32     <bean id="connectionFactory_1" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">33         <property name="connectionFactory">34             <bean class="org.apache.activemq.ActiveMQConnectionFactory">35                 <property name="brokerURL">36                     <value>tcp://192.168.130.13:61616</value>37                 </property>38                 <property name="useAsyncSend">39                     <value>true</value>40                 </property>41             </bean>42         </property>43     </bean>44 45     <!-- 发送消息的目的地(一个队列) -->46     <bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">47         <!-- 设置消息队列的名字 -->48         <constructor-arg index="0" value="activeMQQueue" />49     </bean>50     51     52     <!-- 消息转换 -->53     <bean id="messageConverter" class="com.pis.activeMQ.ObjectMessageConverter"/>54     55     56     <!-- 配置JMS模版 -->57     <bean id="jmsTemplate_1" class="org.springframework.jms.core.JmsTemplate">58         <property name="connectionFactory" ref="connectionFactory" />59         <property name="messageConverter" ref="messageConverter" />60     </bean>61     62     <bean id="jmsTemplate_2" class="org.springframework.jms.core.JmsTemplate">63         <property name="connectionFactory" ref="connectionFactory_1" />64         <property name="messageConverter" ref="messageConverter" />65     </bean>66     67     68     <!-- 生产消息配置 -->69     <bean id="queueProducer" class="com.pis.activeMQ.client.MessageProducer">70         <property name="destination" ref="destination"/>71         <property name="jmsTemplate">  72             <list>  73                 <ref bean="jmsTemplate_1" /> 74                 <ref bean="jmsTemplate_2" /> 75             </list>  76         </property>77     </bean>78     79     80     <!-- 生产消息action bean -->81     <bean id="jmsAction" class="com.pis.action.JmsAction">82         <property name="queueProducer" ref="queueProducer"/>83     </bean>84     85 </beans>
复制代码

转换器如下所示: 转来转去有点恶心的代码 O(∩_∩)O~

复制代码
 1 package com.pis.activeMQ; 2  3 import java.io.ByteArrayInputStream; 4 import java.io.ByteArrayOutputStream; 5 import java.io.IOException; 6 import java.io.ObjectInputStream; 7 import java.io.ObjectOutputStream; 8  9 import javax.jms.JMSException;10 import javax.jms.Message;11 import javax.jms.ObjectMessage;12 import javax.jms.Session;13 14 import org.springframework.jms.support.converter.MessageConversionException;15 import org.springframework.jms.support.converter.MessageConverter;16 17 public class ObjectMessageConverter implements MessageConverter{18 19     20     21     //从消息中取出对象22     @Override23     public Object fromMessage(Message message) throws JMSException,MessageConversionException {24         Object object = null;25         if(message  instanceof ObjectMessage) {26             27             //两次强转,获得消息中的主体对象字节数组流28             byte[] obj = (byte[])((ObjectMessage)message).getObject();29             //读取字节数组中为字节数组流30             ByteArrayInputStream bis = new ByteArrayInputStream(obj);31             try {32                 // 读字节数组流为对象输出流33                 ObjectInputStream ois = new ObjectInputStream(bis);34                 // 从对象输出流中取出对象 并强转35                 object = ois.readObject();36             } catch (Exception e) {37                 e.printStackTrace();38             }39         }40         return object;41     }42 43     44     //将对象转换成消息45     @Override46     public Message toMessage(Object object, Session session) throws JMSException,MessageConversionException {47         ObjectMessage objectMessage = session.createObjectMessage();48         49         ByteArrayOutputStream bos = new ByteArrayOutputStream();//字节数组输出流50         try {51             ObjectOutputStream oos = new ObjectOutputStream(bos);//对象输出流52             53             oos.writeObject(object);//写入对象54             55             byte[] objMessage = bos.toByteArray();//字节数组输出流转成字节数组56             57             objectMessage.setObject(objMessage);//将字节数组填充到消息中作为消息主体 58                 59         } catch (IOException e) {60             e.printStackTrace();61         }62 63         return objectMessage;64     }65     66     67 }
复制代码

生产者 这里用到了原子类来计数,避免使用线程同步,也是第一次接触,convertAndSend方法会调用转换器,把对象转换成消息类型

复制代码
 1 package com.pis.activeMQ.client; 2  3 import java.util.List; 4 import java.util.concurrent.atomic.AtomicInteger; 5  6 import org.apache.activemq.command.ActiveMQQueue; 7 import org.springframework.jms.core.JmsTemplate; 8  9 import com.pis.model.Product;10 11 public class MessageProducer {12     private ActiveMQQueue destination;13     14     private List<JmsTemplate> jmsTemplate;15 16     private Product product;17     18     //原子整型计数(CAS),可以不使用同步19     private AtomicInteger current = new AtomicInteger(0);20     21     //轮询算法解决负载均衡22     private JmsTemplate findJmsTemplate(){23             int cur = current.getAndIncrement();24             int index = cur%jmsTemplate.size();25             return jmsTemplate.get(index);26     }27     28     //发送消息29     public void sendMessage(Product product){30         this.findJmsTemplate().convertAndSend(destination, product);31     }32 33     public ActiveMQQueue getDestination() {34         return destination;35     }36 37     public void setDestination(ActiveMQQueue destination) {38         this.destination = destination;39     }40 41     public List<JmsTemplate> getJmsTemplate() {42         return jmsTemplate;43     }44 45     public void setJmsTemplate(List<JmsTemplate> jmsTemplate) {46         this.jmsTemplate = jmsTemplate;47     }48 49     public Product getProduct() {50         return product;51     }52 53     public void setProduct(Product product) {54         this.product = product;55     }56     57     58     59     60 }
复制代码

服务器端:

xml配置如下:这里只是我本机的服务器配置,另外一台如法炮制,这里用到了监听器,见名知意,大概干嘛用的一看就知道,有消息是会触发监听器,监听器指定使用queueConsumer中的receive方法,这就很清楚了,来一条收一条,来两条收一双。

复制代码
 1 <?xml version="1.0" encoding="UTF-8"?> 2  3 <beans xmlns="http://www.springframework.org/schema/beans" 4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5     xmlns:context="http://www.springframework.org/schema/context" 6     xsi:schemaLocation="http://www.springframework.org/schema/beans   7         http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   8         http://www.springframework.org/schema/context   9         http://www.springframework.org/schema/context/spring-context-2.5.xsd">10 11 12 13     <bean id="connectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">14         <property name="connectionFactory">15             <bean class="org.apache.activemq.ActiveMQConnectionFactory">16                 <property name="brokerURL">17                     <value>tcp://localhost:61616</value>18                 </property>19                 <property name="useAsyncSend">20                     <value>true</value>21                 </property>22             </bean>23         </property>24     </bean>25 26 27     <!-- 发送消息的目的地(一个队列) -->28     <bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">29         <!-- 设置消息队列的名字 -->30         <constructor-arg index="0" value="activeMQQueue" />31     </bean>32     33     34     <!-- 消息转换 -->35     <bean id="messageConverter" class="com.pis.activeMQ.ObjectMessageConverter"/>36 37     <!-- 生产消息配置 -->38     <bean id="queueConsumer" class="com.pis.activeMQ.server.MessageConsumer"/>39 40     41     <bean id="queueListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">42         <constructor-arg ref="queueConsumer"/>43         <property name="defaultListenerMethod" value="receive"/>44         <property name="messageConverter" ref="messageConverter"/>45     </bean>46     47     <bean id="queueListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">48         <property name="connectionFactory" ref="connectionFactory"/>49         <property name="destination" ref="destination" />50         <property name="messageListener" ref="queueListener" />51     </bean>52     53 </beans>
复制代码

消费者 很简洁的代码

复制代码
 1 package com.pis.activeMQ.server; 2  3 import com.pis.model.Product; 4  5 public class MessageConsumer { 6     public void receive(Product product) { 7          8         //如果消费到了就会打印出来 9         System.out.println("server端收到消息:"+product.getName());10     }11 }
复制代码

好了,我这是个struts2+spring的代码,有了第一个xml中的jmsAction的配置,下面就是action的代码

复制代码
 1 package com.pis.action; 2  3 import com.opensymphony.xwork2.ActionSupport; 4 import com.pis.activeMQ.client.MessageProducer; 5 import com.pis.model.Product; 6  7  8 public class JmsAction extends ActionSupport { 9 10     private static final long serialVersionUID = 132132131312L;11     12     private MessageProducer queueProducer;13     14     private Product product;15 16 17     18     @Override19     public String execute() throws Exception {20         System.out.println(product.getName());21         22         queueProducer.sendMessage(product);23         24         return null;25     }26 27     public MessageProducer getQueueProducer() {28         return queueProducer;29     }30 31     public void setQueueProducer(MessageProducer queueProducer) {32         this.queueProducer = queueProducer;33     }34 35     public Product getProduct() {36         return product;37     }38 39     public void setProduct(Product product) {40         this.product = product;41     }42     43     44 }
复制代码

product类,记得要实现serializable方法!

复制代码
 1 package com.pis.model; 2  3 import java.io.Serializable; 4  5  6 public class Product implements Serializable{ 7  8     private String name; 9 10     public String getName() {11         return name;12     }13     public void setName(String name) {14         this.name = name;15     }16     17 }
复制代码

好吧,struts中的配置我就不给了,太简单了,直接访问 localhost:8060/pis/produceMessage.action?product.name=85252 顺便说下,由于懒,这里我就直接把product实体类当成参数传进去了,吼吼,没有界面……懒吧

期间测试可以看http://localhost:8161/admin/queues.jsp 和http://192.168.130.13:8161/admin/queues.jsp可以很明显的看出每次浏览器回车一下,只有其中的一个消息多列一条,完美实现了负载均衡,并且对object类型的message队列学习了一下,收获很大

原创粉丝点击