JMS消息
一、同步、异步消息
应用程序之间的通讯包括同步与异步2种,同步通讯由客户端向远程服务器发起请求并等待远程调用完成后继续执行;异步通讯方式无需阻塞等待对方回应,消息发送方与接收方专注于单一的收发操作,甚至无需知道发送者或接收者是谁。异步通讯机制在现今的互联网开发中得到广泛应用,消息的异步实现可以有效解耦,提高应用程序之间的通讯效率。
简接性是异步消息的关键,消息代理(message broker)和目的地(destination)是异步消息机制中的2个重要概念。消息发送方将消息交由中间件,无需关心消息的接收过程。不同的消息系统具有不同的消息路由模式,但有2种通用的目的地:队列(queue)和主题(topic),对应的消息模型分别是点对点模型(queue)和发布/订阅(主题)。
二、基于JMS的消息
JMS(Java Message Service)是一个Java标准,定义了消息代理的通用API,Spring通过基于模版(JmsTemplate)的抽象为JMS提供了支持,JmsTemplate使得消息的收发过程变得很简单,除此之外,Spring还提供了消息驱动的POJO理念,使得简单的Java对象能够以异步的方式响应队列或主题上的到达的消息。
消息的传递需要消息代理中间件,ActiveMQ是一款成熟的消息中间件,此文采用它作为消息代理,关于它的安装配置及与Spring的集成过程,网上教程很多,不再赘述。
Spring中使用JMS时,首先需要配置连接工厂、创建连接、获得Session然后发送和接收消息,除此之外,还需要声明消息的目的地(队列或主题),采用原生JMS API与消息代理进行交互的过程类似于通过JDBC操作数据库的过程,有许多重复繁琐的步骤,为此,抽象出JmsTemplate,消除了冗长、重复性的代码。使用JmsTemplate如下
- 连接工厂及消息目的地的Bean配置
<!--配置JMS连接工厂--> <bean id="connectionFactory" class="org.apache.activemq.spring.ActiveMQConnectionFactory" p:brokerURL="tcp://localhost:61616"/> <!--声明消息队列--> <bean id="queue" class="org.apache.activemq.command.ActiveMQQueue" p:physicalName="spittle.alert.queue"/> <!--定义消息转换器--> <bean id="mappingJackson2MessageConverter" class="org.springframework.jms.support.converter.MappingJackson2MessageConverter"/> <!--定义JMS template--> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" c:_0-ref="connectionFactory" p:messageConverter-ref="mappingJackson2MessageConverter" p:defaultDestinationName="spittle.alert.queue"/>
配置JmsTemplate需要指定连接工厂、消息转换器和消息目的地,消息转换器在消息发送至消息代理之前,会进行对应格式的转换,其中转换格式可以自定义,Spring提供了多个消息转换器,具体实现细节可以参照Spring官网。
- 发送/接收消息
public class AlertServiceImpl implements AlertService { private JmsOperations jmsOperations; //bean注入 public AlertServiceImpl(JmsOperations jmsOperations) { this.jmsOperations = jmsOperations; } /** * 发送消息 * @param spittle */ public void sendSpittleAlert(Spittle spittle) { jmsOperations.convertAndSend(spittle); } /** * 接收消息 * @return */ public Object retrieveSpittleAlert() { return jmsOperations.receiveAndConvert(); } }
JmsTemplate内部对消息的发送和接收及消息转换操作进行了封装处理,大大简化了消息的收发操作,开发者可以专注于业务,而无需关注底层细节,提高了开发效率。
//发送消息内部实现 public void convertAndSend(Destination destination, final Object message) throws JmsException { this.send(destination, new MessageCreator() { public Message createMessage(Session session) throws JMSException { return JmsTemplate.this.getRequiredMessageConverter().toMessage(message, session); } }); } //接收消息内部实现 public Object receiveAndConvert() throws JmsException { return this.doConvertFromMessage(this.receive()); } protected Object doConvertFromMessage(Message message) { if (message != null) { try { return this.getRequiredMessageConverter().fromMessage(message); } catch (JMSException var3) { throw this.convertJmsAccessException(var3); } } else { return null; } }
- 创建消息驱动的POJO
当JmsTemplate调用receive()方法时会查看队列或主题中是否包含消息,直到收到消息或者等待超时才返回,在这期间,应用必须等待,效率显然是低效的。Spring提供了以POJO的方式处理消息能力,这些消息来自于JMS的队列或是主题当中,POJO就是普通的Java对象,无需继承或实现额外的接口,具有低侵入性的优点。我们可以定义一个纯POJO对象
public class SpittleAlertHandler { public void handleSpittleAlert(Spittle spittle) { System.out.println(spittle.getMessage()); } }
为POJO赋予消息接收能力,需要在Spring中将其配置为消息监听器,配置案例如下:
<jms:listener-container> <jms:listener destination="spittle.alert.queue" ref="spittleHandler" method="handleSpittleAlert" /> </jms:listener-container> <bean id="spittleHandler" class="spittr.alerts.SpittleAlertHandler" />
在这里,我们在消息监听器容器中包含了一个消息监听器,监听器容器监控JMS目的地(队列或主题),一旦有消息到达,它取出消息,然后把它传递给任意一个对此消息感兴趣的消息监听器,消息到达目的时,监听器中定义的method()方法将被触发。消息驱动的POJO能够异步接收消息,使得消息的接收过程得到解藕,应用程序从目的地中(队列或主题)获取消息时无需等待消息到达,实现了消息的异步传递。
三、总结
消息机制在互联网中得到广泛的应用,异步消息使得应用程序之间的通讯得到解耦合,提高了通讯效率,JMS仅是应用程序之间通讯的一种方式,AMQP(高级消息队列协议)为消息定义了线路层的协议,具有多项JMS不具备的优势,它不仅能够跨不同的AMQP实现,还能够跨语言和平台。AMQP具有更加灵活和透明的消息模型,具体内容将以单独的文章呈现,敬请期待。