Apollo是JMS消息通信规范的一个实现,使用activemq需要启动服务的主要过程。按照JMS的规范,我们首先需要获得一个JMS connectionfactory,通过这个connectionfactory来创建connection.在这个基础之上我们再创建session, destination, producer和consumer。因此主要的几个步骤如下:
1). 获得JMS connection factory. 通过我们提供特定环境的连接信息来构造factory。
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user, password, url);
2). 利用factory构造JMS connection
Connection connection = connectionFactory.createConnection();
3). 启动connection
connection.start();
4). 通过connection创建JMS session.
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
5). 指定JMS destination.
Destination destination = session.createTopic("STOCKS.TOPIC-1");
或者
Destination destination = session.createQueue("JOBS.Queue1");
6). 创建JMS producer或者创建JMS message并提供destination.
MessageProducer producer = session.createProducer(null);
7). 创建JMS consumer或注册JMS message listener来接收消息.
MessageConsumer messageConsumer = session.createConsumer(destination);
messageConsumer.setMessageListener(new Listener());
或者直接使用receive()方法主动接收消息
messageConsumer.receive();
8). 发送和接收JMS message.
MapMessage message = session.createTextMessage();
producer.send(destinations, message);
9). 关闭所有JMS资源,包括connection, session, producer, consumer等(调用close方法)
发布订阅模式有点类似于我们日常生活中订阅报纸。每年到年尾的时候,邮局就会发一本报纸集合让我们来选择订阅哪一个。在这个表里头列了所有出版发行的报纸,那么对于我们每一个订阅者来说,我们可以选择一份或者多份报纸。比如北京日报、潇湘晨报等。那么这些个我们订阅的报纸,就相当于发布订阅模式里的topic。有很多个人订阅报纸,也有人可能和我订阅了相同的报纸。那么,在这里,相当于我们在同一个topic里注册了。对于一份报纸发行方来说,它和所有的订阅者就构成了一个1对多的关系。这种关系如下图所示:
如图所示为一个Topic的情况。发布者、主题、订阅者之间都是多对多的关系,即一个发布者可以向多个topic发布消息,一个订阅者可以向多个topic订阅消息。
发布和订阅的消息机制具有以下特征:
• 每个消息可以有多个消费者
• 发布者和订阅者是有时间依赖,只有当前订阅了某个主题的订阅者才能收到消息,订阅者必须保持活跃以获取消息
p2p的过程则理解起来更加简单。它好比是两个人打电话,这两个人是独享这一条通信链路的。一方发送消息,另外一方接收,就这么简单。在实际应用中因为有多个用户对使用p2p的链路,它的通信场景如下图所示:
在p2p的场景里,相互通信的双方是通过一个类似于队列的方式来进行交流。和前面pub-sub的区别在于一个topic有一个发送者和多个接收者,而在p2p里一个queue只有一个发送者和一个接收者。
• 消息的发送者和接收者没有时间上的依赖,接收者可在消息过期前的任意时间内去获取消息,而不一定得发送者是在线的
• 接收者需要确认成功的处理了消息
• 使用 p2p消息机制可确保一个消息只有一个接收者
和前面两种方式比较起来,request-response的通信方式很常见,但是不是默认提供的一种模式。在前面的两种模式中都是一方负责发送消息而另外一方负责处理。而我们实际中的很多应用相当于一种一应一答的过程,需要双方都能给对方发送消息。于是请求-应答的这种通信方式也很重要。它也应用的很普遍。它的通信场景如下图所示:
在JMS里面,如果要实现请求/应答的方式,可以利用JMSReplyTo和JMSCorrelationID消息头来将通信的双方关联起来。另外,QueueRequestor和TopicRequestor能够支持简单的请求/应答过程。
現在,如果我们要实现这么一个过程,在发送请求消息并且等待返回结果的client端的流程如下:请求-应答方式并不是JMS规范系统默认提供的一种通信方式,而是通过在现有通信方式的基础上稍微运用一点技巧实现的。下图是典型的请求-应答方式的交互过程:
// client side
Destination tempDest = session.createTemporaryQueue();
MessageConsumer responseConsumer = session.createConsumer(tempDest);
...
// send a request..
message.setJMSReplyTo(tempDest)
message.setJMSCorrelationID(myCorrelationID);
producer.send(message);
client端创建一个临时队列并在发送的消息里指定了发送返回消息的destination以及correlationID。那么在处理消息的server端得到这个消息后就知道该发送给谁了。Server端的大致流程如下:
public void onMessage(Message request) {
Message response = session.createMessage();
response.setJMSCorrelationID(request.getJMSCorrelationID())
producer.send(request.getJMSReplyTo(), response)
}
这里我们是用server端注册MessageListener,通过设置返回信息的CorrelationID和JMSReplyTo将信息返回。以上就是发送和接收消息的双方的大致程序结构。
回顾前面三种基本的通信方式,我们会发现,他们都存在着一定的共同点,比如说都要初始化ConnectionFactory, Connection, Session等。在使用完之后都要将这些资源关闭。如果每一个实现它的通信端都这么写一通的话,其实是一种简单的重复。
一种简单的方式就是通过工厂方法封装这些对象的创建和销毁,然后简单的通过调用工厂方法的方式得到他们。另外,既然基本的流程都是在开头创建资源在结尾销毁,我们也可以采用Template Method模式的思路。通过继承一个抽象类,在抽象类里提供了资源的封装。所有继承的类只要实现怎么去使用这些资源的方法就可以了。Spring中间的JMSTemplate就提供了这种类似思想的封装。