异步消息是一个应用程序向另一个应用程序间接发送消息的一种方式,这种方式无需等待对方的相应。
异步消息中有两个主要的概念:消息代理(message broker)和目的地(destination)。
当一个应用发送消息时,会将消息发送给一个消息代理。消息代理可以确保被投递到指定的目的地,同时解放发送者,使其能够继续进行其他的业务。
目的地只关注消息应该从哪里获得,而并不关心是谁取走了消息。有两种通用的目的地:队列(queue)和主题(topic),分别对应点对点模型和发布/订阅模型。
在点对点模型中,每一条消息都只有一个发送者和接收者。可以理解为“生产者-消费者”模式。当消息代理得到消息时,它将消息放入一个队列中。当接收者请求队列中的下一条消息时,消息会从队列中取出,并投递给接收者。因为消息投递后会从队列中删除,这样就能保证每条消息只投递给一个接收者。
在发布/订阅消息模型中,消息会发送给一个主题。与队列相同,多个接收者都可以监视一个主题,但与队列不同的是,消息不再是只投递给一个接收者,而是所有的订阅者都会接收到此消息的副本。类似“观察者模式”。
同步消息 | 异步消息 |
---|---|
同步通信意味着等待 客户端通过服务接口与远程服务相耦合 客户端与远程服务的位置相耦合 客户端与服务的可用性相耦合 | 无需等待 面向消息和解耦 位置独立 确保投递 |
Java消息服务(JMS)是一个Java标准,定义了使用消息代理的通用API。类似与JDBC为数据库操作提供的通用接口一样。但JMS同样也和JDBC一样每次使用需要写大量版式代码。
JmsTemplate是Spring提供的一个模板,通过该模板为JMS提供支持。使用JmsTemplate能够非常容易地在消息圣蚕房发送队列和订阅消息,在消费消息的哪一方也能非常容易地接收这些消息。
选取ActiveMQ作为异步消息的消息代理,ActiveMQ是一个很好的开源消息代理产品,在Spring中使用Active之前必须下载并启动其服务。
第一步是配置JMS连接工厂,让JMS知道如何连接到ActiveMQ。ActiveMQConnectionFactory是ActiveMQ自带的连接工厂,在Spring中可以如下配置(p:brokerURL可选,用来指定代理的URL):
<bean id="connectionFactory"
class="org.apache.activemq.spring.ActiveMQConnectionFactory"
p:brokerURL="tcp://localhost:61616"/>
目的地可以是队列,也可以是主题。不论是队列还是主题,都必须使用特定的消息代理实现类在Spring中配置目的地Bean。
声明ActiveMQ队列:
<bean id="queue"
class="org.apache.activemq.command.ActiveMQQueue"
c:_="test.queue"/>
声明ActiveMQ主题:
<bean id="topic"
class="org.apache.activemq.command.ActiveMQTopic"
c:_="test.topic"/>
为了使用JmsTemplate,需要在Spring的配置文件中将它声明为一个bean。配置方法如下:
<bean id="jmsTemplate"
class="org.springfarmework.jms.core.JmsTemplate"
c:_-ref="connectionFactory" />
配置好JmsTemplate后,使用JmsOperation(JmsTemplate所实现的接口)将目标对象发送给消息队列,队列会在稍后得到处理。
首先需要一个JMS对象,然后调用JMS的send方法即可实现发送异步消息。
注意:send()方法的第一个参数是目的地,1.2配置过的。第二个参数是MessageCreator类型的匿名对象,需要实现createMessage方法。
public class AlterService{
@Autowired
private JmsOperations jmsOperations; //注入JMS模板
public void sendString(String str){
jmsOperations.send( //调用方法发送消息
"test.queue", //指定目的地
new MessageCreator(){ //send方法需要一个MessageCreator作为参数,这里使用匿名类实现
public Message createMessage(Session session) throws JMSException{ //需要实现该方法
return session.createObjectMessage(string); //创建消息
}
}
);
}
}
2.2.1中send方法的第一个参数指定目的地,其实可以在配置JmsTemplate时指定默认目的地,这样就可以省去send的第一个参数。
<bean id="jmsTemplate"
class="org.springfarmework.jms.core.JmsTemplate"
c:_-ref="connectionFactory"
p:defaultDestinationName="test.queue" />
convertAndSend()方法并不需要MessageCreator参数,这是因为该方法会使用内置的消息转换器创建消息。所以使用convertAndSend()方法时代码非常简洁:
public void sendString(String string){
jmsOperations.converAndSend(string);
}
接收消息比发送消息更为简单,只需要调用JmsTemplate的receice()方法即可。当调用该方法时。JmsTemplate会尝试从消息代理中获取一个消息。如果没有消息receice()方法会一直等待,知道获取方法为止。
public String receiceString(){
try{
return (String) jmsOperations.receive();
}catch (JMSException jmsException){
throw JmsUtils.convertJmsAccessException(jmsException);
}
}