与远程化机制类似,JMS也是应用之间通讯的方式。不同在于信息在系统间传输的方式。像RMI,Hessian/Burlap这样的远程化机制都是同步的。客户调用一个远程方法之后必须等待方法返回之后才能继续执行下去。即使远程方法并不返回任何值,客户仍然需要等待。
与之不同,JMS支持Java应用程序间的异步通讯。即消息以异步方式传输时,客户不必等待服务返回,而是继续执行。JMS异步通讯比起同步通讯有很多优势,不过现在先让我们看看如何利用JMS发送消息。
1. 架构JMS
我们都使用过邮政服务。邮政服务的核心在于它是间接的。同样的,JMS的核心也在于此。当一个应用发送信息给另一个应用,它们之间并没有直接的联系。发送方只是将消息发送到某个服务那里,该服务会确保发送给接收方。
这里就涉及到了JMS中两个重要的概念:消息代理(message broker)和目的地(destination)。一个应用发送消息,它会将消息先发送到一个message broker,然后message broker会把消息发送到指定目标处,该过程sender不需要参与。
JMS中的消息地址就是destination。Destination只关心消息在何处被接收,不关心接收人是谁。JMS有两类destination:队列(queue)和话题(topic)。queue对应于点对点(point-to-point)的消息模型,而topic对应于订阅-发布(publish-subscribe)的消息模型。
点对点模型
该模型规定每条消息只有一个sender和一个receiver。message broker接收到消息后将消息置于一个queue中,然后receiver将queue中的消息取走。这样就可以保证消息只会被发送给一个receiver。
尽管消息队列中的消息只会发送给一个receiver,但这并不意味着只有一个receiver从queue中取消息。经常有多个receiver,只不过它们只取属于它们的消息罢了。在JMS中,如果有多个receiver都在监听queue,那么对于某条消息来说,我们无法知道哪个receiver将会处理它。这种方式有它的好处,因为只需要向queue添加一个listerner就可以相应地增加应用的消息处理能力了。
发布-订阅模型
在publish-subcribe模型中,消息被发送到一个topic。和queue一样,有很多receiver都会监听该topic。不过一个topic的所有订阅者都会接收到消息的副本。发布者并不需要知道订阅者是谁,它只知道消息将会被发布到一个特定的topic,但不知道谁在监听这个topic。这也意味着发布者并不知道消息是怎么被处理的。
下面让我们看看JMS消息机制与同步RPC的区别。
2. JMS的优点
同步通讯看似简单易用,但对远程服务的客户也有诸多限制,例如:
·同步通讯需要等待;
·客户通过服务接口与服务实现紧耦合。如果接口变更了,所有客户都需要做相应地变更。
·客户与服务地址构成紧耦合。客户必须配置服务的网络地址,这样它才知道如何联系到服务。如果网络拓扑变更了,那么客户就需要重新配置。
·客户与服务可用性构成紧耦合。如果一个服务不可用了,那么客户也就失效了。
如果你需要考虑这些限制,那么你可能就会采用诸如JMS的异步通讯方式。
不等待
当使用JMS发送消息时,客户不需要等待。它只会把消息发给message broker,让message broker来负责发送消息给正确的目的地。因为不需要等待,客户就可以继续指定其他操作,因此性能可以得到极大地提升。
面向消息(message-oriented)
RPC是面向方法调用的,JMS的消息发送是以数据为中心的。这就说明客户没有绑定到某个具体的方法上。任何可以处理客户数据的queue或topic订阅者都可以处理消息。客户不需要知道其他的服务细节。
地址无关
同步RPC服务地址通常都是用网络地址来标识的,因此客户必然不喜欢修改网络拓扑。如果一个服务的IP发生变更或配置为监听另一个端口,客户就必须做相应的调整否则就无法再访问服务。
相反地,JMS客户并不知道谁来处理消息以及服务地址,它只知道消息要被发送到得queue或topic。因此,只要是从queue或topic里面取消息,那么就不需要知道服务地址。
我们可以利用点对点的模型的地址无关性来创建服务的集群。如果客户不知道服务地址并且如果服务必须要能够访问message broker,那么就应该允许多个服务能够从同一个queue中取消息。如果服务负担过重无法处理,我们所需的就是添加一些新的服务实例来监听同一个queue。
地址无关性在发布-订阅模型中有一个很有趣的副作用。多个服务都可以订阅到一个topic,都接收同一个消息的重复的副本。但是每个服务处理消息的方式都是不同的。
发布
在同步通讯服务中,服务必须监听某个端口的IP。如果服务down掉了或者不可用了,客户也就不能再使用了。但是如果使用JMS发送消息,客户就可以不用担心消息发送。当消息发送时,即使服务不可用了,它将会被保存下来直到服务再次可用。
下面我们来介绍一种流行的message broker:ActiveMQ。
3. Spring的ActiveMQ使用
ActiveMQ是一个很好的开源message broker,很适用于JMS异步传输消息。如果要使用ActiveMQ,你需要下载它,并确保incubator-activemq-4.1.0.jar在你的classpath中。
创建连接工厂
Spring有多种方法使用JMS发送和接收消息,不过我们都需要一个JMS连接工厂。这个连接工厂通过message broker来发送消息。因为选定了ActiveMQ作为我们的message broker,我们就需要配置JMS连接工厂,这样它才知道如何连接ActiveMQ。ActiveMQConnectionFactory是JMS的连接工厂类,Spring中配置如下:
<bean id="connectionFactory"
class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
</bean>
brokerURL属性表明了message broker的地址。
声明ActiveMQ消息目的地
除了需要一个连接工厂,我们还需要设置destination,它可以是queue或topic,具体依赖于应用的需要。无论你是否在使用queue或topic,你必须使用message broker的特定实现类配置destination bean。例如,下面的<bean>即声明了ActiveMQ queue:
<bean id="rantzDestination"
class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg index="0" value="rantz.marketing.queue" />
</bean>
同样地,下面的<bean>声明了ActiveMQ的topic:
<bean id="rantzDestination"
class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg index="0" value="rantz.marketing.topic" />
</bean>
如果你使用不用的message broker,你需要利用相应的message broker实现来配置destination bean。