1 JMS介绍
JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或者在分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。 JMS源于企业应用对于消息中间件的需求,使应用程序可以通过消息进行异步处理而互不影响。Sun公司和他的合作伙伴设计的JMS API定义了一组公共的应用程序接口和相应语法,使得Java程序能够和其它消息组件进行通信。JMS可以使你通过消息收发服务(有时称为消息中介程序或路由器)从一个JMS客户机向另一个JMS客户机发送消息,JMS和JDBC很像。
2 JMS体系结构
JMS由以下元素组成:
2.1 JMS提供商
连接面向消息中间件的,JMS接口的一个实现。提供者可以是Java平台的JMS实现,也可以是非Java平台的面向消息中间件的适配器。JMS服务提供者实现消息队列和通知,同时实现消息管理API。JMS已经是J2EE API的一部分了,J2EE服务器提供JMS服务。
2.2 JMS客户端
生产或消费基于消息的Java的应用程序或对象。
2.3 JMS生产者
创建并发送消息的JMS客户。
2.4 JMS消费者
接收消息的JMS客户。
2.5 JMS消息
包括可以在JMS客户之间传递数据的对象。消息是服务提供者和客户端之间传递信息所使用信息单元。JMS消息由以下三部分组成:
- 消息头(header)—JMS消息头包含许多字段,它们是消息发送后由JMS提供者或消息发送者产生的,用来表示消息、设置优先权和失效时间等等,并且为消息确定路由。
- 属性(property)—用来添加删除消息头以外的附加消息。
- 消息体(body)—JMS中定义了5种消息体:ByteMessage、MapMessage、ObjectMessage、StreamMessage和TextMessage。
2.6 JMS队列
一个容纳那些被发送的等待阅读的消息的区域。与队列名字所暗示的意思不同,消息的接受顺序并不一定要与消息的发送顺序相同。一旦一个消息被阅读,该消息将被从队列中移走。
2.7 JMS主题
一种支持发送消息给多个订阅者的机制。
3 JMS的消息传送模式
JMS定义了两种通用的消息传输模式,分别是点对点(P2P),发布/订阅(Pub/Sub)。
3.1 点对点消息传送模型(P2P)
在点对点消息传送模型中,应用程序由消息队列,发送者,接收者组成。每一个消息发送给一个特殊的消息队列,该队列保存了所有发送给它的消息(除了被接收者消费掉的和过期的消息)。
点对点消息模型有如下特性:
-
每个消息只有一个接受者,消息一旦被接收就不再保留在队列里面。
-
消息发送者和消息接受者并没有时间依赖性,当消息发送者发送消息的时候,无论接收者程序在不在运行,都不会影响消息发送到队列。
-
一个队列无论被多少个接收者监听,每条消息只会被一个接收者接收。
-
消息存在先后顺序(消息按照发送的顺序存放,消息接收时也是按照这个顺序,除非使使用了消息优先级)。
-
当接收者收到消息的时候,会发送确认收到通知(acknowledgement)
点对点消息模型图:
3.2 发布/订阅消息传送模型(Pub/Sub)
在发布/订阅消息模型中,发布者发布一个消息,该消息通过主题(topic)传递给所有的客户端。在这种模型中,发布者和订阅者彼此不知道对方,是匿名的且可以动态发布和订阅Topic。Topic主要用于保存和传递消息,且会一直保存消息直到消息被传递给客户端。
发布/订阅消息模型有如下特性:
-
一个消息可以传递给多个订阅者
-
发布者和订阅者有时间依赖性,只有当客户端创建订阅后才能接受消息,且订阅者需一直保持活动状态以接收消息。
-
JMS允许订阅者创建可持久化的订阅,这样即使订阅者没有运行也能接收到所订阅的消息。
-
每条消息都会传递给该主题下的所有订阅者。
-
通常发布者不知道也意识不到哪一个订阅者正在接收消息。
发布/订阅消息模型图:
4 JMS的应用程序接口
ConnectionFactory 接口(连接工厂) ConnectionFactory是创建Connection对象的工厂,根据不同的消息类型可以选择用队列连接工厂或者主题连接工厂,分别对应QueueConnectionFactory和TopicConnectionFactory.可以通过JNDI来查找ConnectionFactory。
Connection 接口(连接) 连接代表了应用程序和消息服务器之间的通信链路(实际是对TCP/IP Socket的封装)。Connection可以产生一个或多个Session,同样有两种类型的Connection,分别是QueueConnection和TopicConnection。
Destination 接口(目标) Destination是一个包装了消息目的地标识符的受管对象,消息目的地是指消息发布和接收的地点,或者是队列,或者是主题,可以通过JNDI发现它们。有两种类型的Destination,分别是Queue和Topic。
Session 接口(会话) 表示一个单线程的上下文,用于发送和接收消息。由于会话是单线程的,所以消息是连续的,就是说消息是按照发送的顺序一个一个接收的。会话的好处是它支持事务。如果用户选择了事务支持,会话上下文将保存一组消息,直到事务被提交才发送这些消息。在提交事务之前,用户可以使用回滚操作取消这些消息。Session可以创建生产者,消费者,消息等,同时也有两种类型的Session:QueueSession和TopicSession。
MessageConsumer 接口(消息消费者) 由Session创建的对象,用于接收发送到Destination的消息。消费者可以同步地(阻塞模式),或(非阻塞)接收队列和主题类型的消息。消费者有两种类型:QueueReceiver和TopicSubsriber。
MessageProducer 接口(消息生产者) 由Session创建的对象,用于发送消息到Destination。消息生产者有两种类型:QueueSender和TopicPublisher。
Message 接口(消息) 是在消费者和生产者之间传送的对象,也就是说从一个应用程序传送到另一个应用程序。
MessageListener(消息监听器)
消息消费者可以通过注册MessageListener接口异步的接收消息。
JMS的开发步骤:
5 JMS的消息结构
JMS Message消息由三部分组成:
-
消息头
-
消息体
-
消息属性
5.1 消息头
JMS消息头预定义了若干字段用于客户端与JMS提供者之间识别和发送消息,预编译头如下:
名称 | 描述 |
---|---|
JMSDestination | 消息发送的 Destination,在发送过程中由提供者设置 |
JMSMessageID | 唯一标识提供者发送的每一条消息。这个字段是在发送过程中由提供者设置的。 |
JMSDeliveryMode | 消息持久化。包含值 DeliveryMode.PERSISTENT 或者 DeliveryMode.NON_PERSISTENT。 |
JMSTimestamp | 提供者发送消息的时间,由提供者在发送过程中设置 |
JMSExpiration | 消息失效的时间,毫秒,值 0 表明消息不会过期,默认值为0 |
JMSPriority | 消息的优先级,由提供者在发送过程中设置。优先级 0 的优先级最低,优先级 9 的优先级最高。0-4为普通消息,5-9为加急消息。优先级高就一定先发送,只保证了加急消息必须先于普通消息发送。默认值为4 |
JMSCorrelationID | 通常用来链接响应消息与请求消息,由发送消息的 JMS 程序设置。 |
JMSReplyTo | 请求程序用它来指出回复消息应发送的地方,由发送消息的 JMS 程序设置 |
JMSType | JMS 程序用它来指出消息的类型。 |
JMSRedelivered | 消息的重发标志,false,代表该消息是第一次发送,true,代表该消息为重发消息。 |
注意:
不过需要注意的是,在传送消息时,消息头的值由JMS提供者来设置,因此开发者使用以上setJMSXXX()方法分配的值就被忽略了,只有以下几个值是可以由开发者设置的:JMSCorrelationID,JMSReplyTo,JMSType
5.2 消息体
在消息体中,JMS API定义了五种类型的消息格式,让我们可以以不同的形式发送和接受消息,并提供了对已有消息格式的兼容。不同的消息类型如下:
-
TextMessage--一个字符串对象
-
MapMessage--一套名称-值对
-
ObjectMessage--一个序列化的 Java 对象
-
BytesMessage--一个字节流数据
-
StreamMessage -- Java原始值的数据流
5.3 消息属性
消息属性以属性名和属性值对的形式制定的。可以将属性是为消息头得扩展,属性指定一些消息头没有包括的附加信息,比如可以在属性里指定消息选择器。消息的属性就像可以分配给一条消息的附加消息头一样。它们允许开发者添加有关消息的不透明附加信息。它们还用于暴露消息选择器在消息过滤时使用的数据。
消息属性按类型可以分:
-
标准属性(
JMSX
作为前缀) -
消息组件自定义的属性(
JMS_
作为前缀) -
应用自定义的属性。自定义的属性不要以前面两种为前缀。
消息属性可以通过Message接口的setXXXProperty
方法进行设置,包括了多数的基本类型及其包装类型。
下面是JMS标准属性介绍:
其中,JMSXGroupID和JMSGroupSeq是客户端可以设置的。消息的属性一旦设置,且消息发送后,就会变成只读的,将不能再被修改。
跟头信息的设置不同的是,对于这些标准属性,JMS并不要求消息中间件必须设置这些属性,所以这些属性的值可能是存在的,也可能为空,而且经测试大部分的标准属性都可以由应用程序自行设置,就像自定义的属性一样。
通过如下方法查看JMS属性:
connection.getMetaData().getJMSXPropertyNames();
6 消息的可靠性机制
6.1 消息接收确认
JMS消息只有在被确认之后,才认为已经被成功的消费了。消息的成功消费通常包含三个阶段:客户接收消息、客户处理消息和消息被确认
在事务性会话中,当一个事务被提交的时候,确认自动发生。在非事务会话中,消息何时被确认取决于创建会话时的应答模式(acknowledgement mode)。该参数有以下三个可选值:
-
Session.AUTO_ACKNOWLEDGE:当客户成功的从receive方法返回的时候,或者从MessageListener.onMessage方法返回的时候,会话自动确认客户客户收到的信息
-
Session.CLIENT_ACKNOWLEDGE:客户通过调用Message的acknowledge方法确认消息。需要注意的是,在这种模式中,确认是在会话层上进行,确认一个被消费的消息将自动确认所有已被会话消费的消息。例如:如果一个消息消费者消费了10个消息,然后确认第5个消息,那么所有10个消息都被确认
-
Session.DUPS_OK_ACKNOWLEDGE:该选择只是会话迟钝的确认消息的提交。如果JMS provider失败,那么可能会导致一些重复的消息。如果是重复的消息,那么JMS provider必须把消息头的 JMSRedelivered 字段设置为true
6.2 消息持久性
JMS支持以下两种消息提交模式:
- PERSISTENT:指示JMS provider持久保存消息,以保证消息不会因为JMS provider的失败而丢失
- NON_PERSISTENT:不要求JMS provider持久保存消息
6.3 消息优先级
可以使用消息优先级来指示JMS provider首先提交紧急的消息。优先级分10个级别,从0(最低)到9(最高)。如果不指定优先级,默认级别是4。需要注意的是,JMS provider并不一定保证按照优先级的顺序提交消息。
6.4 消息的过期
可以设置消息在一定时间后过期,默认永久不过期。
6.5 消息的临时目的地
可以通过会话上的 createTemporaryQueue 方法和 createTemporaryTopic 方法来创建临时目的地。它们的存在时间只限于创建它们的连接所保持的时间。只有创建该临时目的地的连接上的消息消费者才能够从临时目的地中提取消息。
6.6 持久订阅
首先消息生产者必须使用PERSISTENT提交消息。客户可以通过会话上的createDurableSubscriber方法来创建一个持久订阅,该方法的第一个参数必须是一个topic。第二个参数是订阅的名称
JMS provider会存储发布到持久订阅对应的topic上的消息。如果最初创建持久订阅的客户或者任何其他客户,使用相同的连接工厂和连接的客户ID,相同的主题和相同的订阅名,再次调用会话createDurableSubscriber方法,那么该持久订阅就会被激活。JMS provider会向客户发送客户处于非激活状态时所发布的消息
持久订阅在某个时刻只能有一个激活的订阅者。持久订阅在创建之后会一直保留,直到应用程序调用会话上的unsubscribe方法
6.7 本地事务
在一个JMS客户端,可以使用本地事务来组合消息的发送和接收。JMS Session接口提供了commit和rollback方法。事务提交意味着生产的所有消息被发送,消费的所有消息被确认;事务回滚意味着生产的所有消息被销毁,消费的所有消息被恢复并重新提交,除非它们已经过期。 事务性的会话总是牵涉到事务处理中,commit或rollback方法一旦被调用,一个事务就结束了,而另一个事务被开始。关闭事务性会话将回滚其中的事务。 需要注意的是,如果使用请求/回复机制,即发送一个消息,同时希望在同一个事务中等待接收该消息的回复,那么程序将被挂起,因为直到事务提交,发送操作才会真正执行。 需要注意的还有一个,消息的生产和消费不能包含在同一个事务中。
在事务状态下进行发送操作,消息并未真正投递到中间件,而只有进行session.commit操作之后,消息才会发送到中间件,再转发到适当的消费者进行处理。如果是调用rollback操作,则表明,当前事务期间内所发送的消息都取消掉。
网友评论