1. JMS即Java消息服务(Java Message Service)应用程序接口, 是一个Java平台中关于面向消息中间件(Message Oriented Middleware)的API, 用于在两个应用程序之间或分布式系统中发送消息, 进行异步通信。
2. JMS是一种与厂商无关的API, 它类似于JDBC(Java Database Connectivity)。这里JDBC是可以用来访问许多不同种类的关系型数据库的API, 而JMS可以访问包括IBM提供的MQSeries和apache提供的ActiveMQ等许多厂商提供的消息收发服务。
3. JMS应用程序由以下部分组成
3.1. JMS提供者是一个消息系统, 实现了JMS接口并提供管理和控制功能。(例如: ActiveMQ)
3.2. JMS客户端是用Java编程语言编写的程序或组件, 它们可以生成和使用消息。
3.3. 管理对象是管理员为客户端使用而创建的预配置JMS对象。有两种JMS管理对象, 分别是目的地和连接工厂。
3.4. 消息是在JMS客户端之间传递信息的对象。
3.5. JMS生产者, 创建并发送消息的JMS客户。
3.6. JMS消费者, 接收消息的JMS客户。
3.7. 点对点消息传递域, 一个容纳那些被发送的等待被阅读的消息的队列。与队列名字所暗示的意思不同, 消息的接受顺序并不一定要与消息的发送顺序相同。一旦一个消息被阅读, 该消息将被从队列中移走。
3.8. 发布/订阅消息域(JMS主题), 一种支持发送消息给多个订阅者的机制。
4. 点对点消息模型
4.1. 点对点模型用于消息生产者和消息消费者之间的通信。在点对点消息传送模型中, 应用程序由消息队列(Queue)、生产者(Producer)和消费者(Consumer)组成。消息生产者将消息发送到由某个特定名字标示的消息队列(Queue)中, 消息消费者从这个特定队列中获取对应消息。队列保留发送给它的所有消息, 直到消息被消耗或过期。
4.2. PTP消息传递具有以下特征
4.2.1. 每条消息只有一个使用者。
4.2.2. 消息的发送者和接收者没有时间依赖性。也就是说, 当发送者发送了消息时, 不管接收者有没有正在运行, 它不会影响消息被发送到消息队列中。同时, 接收者接收消息的时候, 发送者也不必同时运行。
4.2.3. 当接收者收到消息的时候, 会发送确认收到通知(acknowledgement)到消息队列。
4.3. 模型图
5. 发布订阅消息模型
5.1. 在发布与订阅消息传送模型中, 应用程序由主题(Topic)、发布者(Publisher)和订阅者(Subscriber)组成。在这种模型中, 发布者和订阅者通常是匿名的, 且可以动态发布和订阅主题(Topic)。该系统负责将主题的多个发布者送达的消息分发到其多个订阅者。主题仅保留将消息分发给当前订阅者所需的时间。
5.2. 发布/订阅消息传递具有以下特征
5.2.1. 每个消息可以有多个使用者。
5.2.2. 发布者和订阅者具有时间依赖性。针对某个主题(Topic)的订阅者, 它必须创建一个订阅者之后, 才能消费发布者的消息, 而且为了消费消息, 订阅者必须保持运行的状态。
5.2.3. 为了缓和这样严格的时间依赖性, JMS允许订阅者创建一个可持久化的订阅。这样, 即使订阅者没有在运行状态, 它也能在再次运行的时候接收到发布者的消息。
5.3. 模型图
6. JMS的消息消费
6.1. 消息产品本质上是异步的, 消息的产生和消费之间没有基本的时序依赖性。但是, JMS规范在更精确的意义上使用了该术语。可以通过以下两种方式之一来使用消息:
6.1.1. 同步: 通过调用消费者的receive方法从目的地显式的提取消息, receive方法在接收到消息之前或超时之前将一直阻塞。
6.1.2. 异步: 客户端可以向使用者注册消息侦听器。消息监听器类似于事件侦听器。每当消息到达目的地时, JMS提供程序都会通过调用监听器的onMessage方法来传递消息, 客户端不必请求消息即可接收它们, 该方法将对消息的内容进行操作。
7. 连接工厂是用来创建客户和提供者之间的连接。连接工厂封装了一组由管理员定义的连接配置参数。每个连接工厂都是ConnectionFactory、QueueConnectionFactory或TopicConnectionFactory接口的实例。
8. JMS连接
8.1. Connection表示在客户端和JMS提供者之间建立的连接。跟ConnectionFactory一样, Connection也有QueueConnection和TopicConnection两种类型的对象。
8.2. 每个连接都是Connection、QueueConnection或TopicConnection接口的实例。当您有一个ConnectionFactory对象时, 可以使用它来创建一个连接。
Connection connection = connectionFactory.createConnection();
QueueConnection connection = connectionFactory.createQueueConnection();
TopicConnection connection = connectionFactory.createTopicConnection();
8.3. 在应用程序完成之前,必须关闭所有已创建的连接。没有关闭连接可能导致JMS提供程序无法释放资源。关闭连接也会关闭其会话以及它们的消息产生者和消息使用者。
connection.close();
8.4. 在您的应用程序可以使用消息之前, 您必须调用连接的start方法。
connection.start();
9. JMS会话
9.1. JMS会话(Session)是用于制造和使用消息的单线程上下文。同样, Session也有QueueSession和TopicSession两种类型的对象。
9.2. 您使用会话创建以下内容:
9.2.1. 消息生产者
9.2.2. 消息消费者
9.2.3. 消息内容
9.2.4. 队列浏览器
9.2.5. 临时队列和主题
9.3. 会话可以提供了一个事务上下文, 在该上下文中, 一组发送和接收被组合到了一个原子操作中。
9.4. 创建Connection对象后, 可以使用它来创建Session, 第一个参数表示会话未使用事务; 第二种参数是会话在成功接收到消息后会自动对其进行确认。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
9.5. 要创建事务会话, 请使用以下代码, 第一个参数表示会话使用事务; 第二个参数指示未为事务会话指定消息确认。第二个参数是Session.SESSION_TRANSACTED时, 第一个参数必须是true。
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
9.6. JMS的可靠性机制
9.6.1. JMS消息只有在被确认之后, 才认为已经被成功地消费了。消息的成功消费通常包含三个阶段: 客户接收消息、客户处理消息和消息被确认。
9.6.2. 在事务性会话中, 当一个事务被提交的时候, 确认自动发生。在非事务性会话中, 消息何时被确认取决于创建会话时的应答模式(acknowledgement mode)。
9.6.3. 该参数有以下三个可选值
- Session.AUTO_ACKNOWLEDGE: 当客户成功的从receive方法返回的时候或者从MessageListener.onMessage方法成功返回的时候, 会话自动确认客户收到的消息。
- Session.CLIENT_ACKNOWLEDGE: 客户主动给的调用消息的acknowledge方法确认消息。需要注意的是, 在这种模式中, 确认是在会话层上进行。确认一个被消费的消息将自动确认所有已被会话消费的消息。例如, 如果一个消息消费者消费了10个消息, 然后确认第5个消息, 那么所有10个消息都被确认。
- Session.DUPS_OK_ACKNOWLEDGE: 该选择只是会话延迟的确认消息的提交。如果JMS provider失败, 那么可能会导致一些重复的消息。不过使用此种模式, 可以减轻会话的负载。
10. 消息目的地(Destination), 即消息生产者的消息发送目标或者说消息消费者的消息来源。在PTP消息传递域中, 目的地称为队列。在发布/订阅消息传递域中, 目的地称为主题。 同样, Destination也有Queue和Topic两种类型的对象。
11. 消息生产者
11.1. 消息生产者(MessageProducer), 由Session创建, 并用于将消息发送到目的地。同样, MessageProducer也有QueueSender和TopicPublisher两种类型的对象。
11.2. 使用会话为目的地创建生产者。以下示例说明您可以为Destination、Queue或Topic对象创建生产者。
MessageProducer messageProducer = session.createProducer(dest);
QueueSender sender = session.createSender(queue);
TopicPublisher publisher = session.createPublisher(topic);
11.3. 可以通过将null指定为createProducer的参数来创建身份不明的生产者。对于身份不明的生产者, 在发送消息的时候指定目的地。
MessageProducer messageProducer = session.createProducer(null);
11.4. 创建消息产生器之后, 可以使用send方法发送消息
MessageProducer nonDesProducer = session.createProducer(null);
nonDesProducer.send(destination, message);
MessageProducer producer = session.createProducer(destination);
producer.send(message);
12. 消息消费者
12.1. 消息消费者(MessageConsumer), 由Session创建, 用于接收目的地的消息。MessageConsumer也有QueueReceiver和TopicSubscriber两种类型的对象。
12.2. 可以使用Session为MessageConsumer、Queue或Topic对象创建消息消费者。
MessageConsumer messageConsumer = session.createConsumer(dest);
QueueReceiver receiver = session.createReceiver(queue);
TopicSubscriber subscriber = session.createSubscriber(topic);
12.3. 可以通过session的creatDurableSubscriber方法来创建持久化的订阅者。仅当您使用主题时, 此方法才有效。
13. 消息侦听器
13.1. 消息侦听器是充当消息的异步事件处理程序的对象。此对象实现MessageListener接口, 该接口包含一个方法onMessage。在onMessage方法中, 消息到达时要执行的操作。
13.2. 您可以使用setMessageListener方法向特定的MessageConsumer注册消息侦听器。
MessageConsumer consumer = session.createConsumer(destination);
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message msg) {
}
});
13.3. 注册消息侦听器后, 您可以在Connection上调用start方法以开始传递消息。如果在注册消息侦听器之前调用start, 则可能会丢失消息。
13.4. 每当传递消息时, JMS提供程序都会自动调用消息侦听器的onMessage方法。onMessage方法采用一个Message类型的参数消息, 其可以转换为其它真实类型的消息。
13.5. 消息侦听器并不仅仅监听一个特定类型的目标。同一侦听器可以从队列或主题中获取消息, 具体取决于为其创建消息使用者的目的地的类型。但是, 消息侦听器通常期望特定的消息类型和格式。
13.6. 您的onMessage方法应处理所有异常, 如果抛出RuntimeException被认为是编程错误。
14. JMS消息选择器
14.1. 如果您的消息传递应用程序需要过滤接收到的消息, 则可以使用JMS API消息选择器, 该消息选择器允许消息使用者指定对其感兴趣的消息。消息选择器将过滤消息的工作分配给JMS提供程序而不是应用程序。
14.2. 消息选择器是一个包含表达式的字符串。表达式的语法基于SQL92条件表达式语法的子集。
14.3. 创建消息使用者时, 可以使用createConsumer和createDurableSubscriber方法将消息选择器指定为参数。
14.4. 消息使用者仅接收其消息头或属性与选择器匹配的消息。消息选择器无法根据消息正文的内容选择消息。
15. JMS消息
15.1. JMS应用程序的最终目的是生成和使用消息, 然后其他软件应用程序可以使用这些消息。JMS消息具有简单但高度灵活的基本格式, 允许您创建在各个平台上的非JMS应用程序所使用的格式匹配的消息。
15.2. JMS消息可以包含三个部分: 消息头, 属性和主体。仅消息头是必需的。
15.3. 消息头(必须)
15.3.1. JMS消息头包含许多预定义的字段, 这些字段包含客户端和提供者用来标识和路由消息的值。
15.3.2. 每个消息头字段都具有关联的setter和getter方法, 这些方法记录在Message接口的说明中。某些消息头字段旨在由客户端设置, 但许多消息头字段由send或publish方法自动设置, 这会覆盖任何客户端设置的值。
15.3.3. JMSDestination: 消息发送的目的地, 主要是指Queue和Topic, 该字段由生产者的send或publish方法设置。
15.3.4. JMSDeliveryMode: 传送模式, 有持久模式和非持久模式两种, 默认是持久化消息。一条持久性的消息, 当JMS提供者出现故障时, 该消息并不会丢失, 它会在服务器恢复之后可以重新传递。一条非持久的消息, 当服务器出现故障时, 该消息将永远丢失。该字段由生产者的send或publish方法设置。
15.3.5. JMSExpiration: 消息的过期时间, 是send或publish方法中的timeToLive值加上发送时刻的GMT时间值的和。如果timeToLive值等于零, 则JMSExpiration被设置为零, 表示该消息永不过期, 默认就是永久不过期。如果发送后, 在消息过期时间之后消息还没有被消费, 则该消息被清除。该字段由send或publish方法设置。
15.3.6. JMSPriority: 消息优先级, 从0-9十个级别, 0-4是普通消息, 5-9是加急消息。JMS不要求JMS Provider严格按照这十个优先级发送消息, 但必须保证加急消息要先于普通消息到达, 默认是4级。该字段由send或publish方法设置。
15.3.7. JMSMessageID: 唯一识别每个消息的标识, 该字段由生产者的send或publish方法设置。
15.3.8. JMSTimestamp: 它是消息被发送给提供者的时间。该字段由生产者的send或publish方法自动设置。
15.3.9. JMSCorrelationID: 用来连接到另外一个消息, 典型的应用是在回复消息中连接到原消息。在大多数情况下, JMSCorrelationID用于将一条消息标记为对JMSMessageID标示的上一条消息的应答, 不过, JMSCorrelationID可以是任何值, 不仅仅是JMSMessageID。该字段由客户端设置。
15.3.10. JMSReplyTo: 提供本消息回复消息的目的地址。该字段由客户端设置。
15.3.11. JMSType: 消息类型的标识符, JMS API并没有定义该字段的一个标准, 可以根据应用程序的实际需求设计该字段, 来标识应用程序是否使用该类型的消息。该字段由客户端设置。
15.3.12. JMSRedelivered: 如果一个客户端收到一个设置了JMSRedelivered属性的消息, 则表示可能客户端曾经在早些时候收到过该消息, 但并没有签收(acknowledged)。如果该消息被重新传送, JMSRedelivered=true否则JMSRedelivered=flase。该字段由提供者设置。
15.4. 消息头的这些字段, 我们可以按照需求对它进行设计, 再使用生产者的send或publish方法将其发送到消息服务上。
16. 消息属性(可选): 如果您需要除消息头字段提供的值以外的其它值, 则可以创建和设置消息的属性。您可以使用属性来提供与其它消息传递系统的兼容性, 也可以使用它们来创建消息的选择器。
17. 消息体(可选): 允许用户创建五种类型的消息: TextMessage(文本消息)、MapMessage(映射消息)、BytesMessage(字节消息)、StreamMessage(流消息)和(ObjectMessage)对象消息。消息接口非常灵活, 并提供了许多方式来定制消息的内容。