JMS是什么
JMS Java Message Service,是一个Java消息服务,是J2EE中的一个技术
JMS规范
定义了Java访问消息中间件的接口,但是并没有给予实现。那些实现了JMS接口和JMS规范的称为JMS Provider 例如:ActiveMq
JMS Provider
实现了JMS接口和规范的消息中间件
JMS Message
JMS的消息,由三部分组成:1. 消息头 2. 消息属性 3. 消息体(具体的消息数据)
JMS Producer
消息生产者,创建和发送JMS消息的客户端应用
JMS Consumer
消息消费者,是接收和处理JMS消息的客户端应用
消费者有两种消费方式:
1. 同步消费:消费者通过调用receive方法,从队列中显示的提取消息,如果队列中没有消息到达,那么receive方法会阻塞,直到消息到达
2. 异步消费:消费者向消息中间件注册一个消息监听器,定义当有消息到达的时候应当采取的动作,这样,当有消息到达的时候,消息中间件通过回调的方式来处理
JMS domains
消息传递域,JMS规范中定义了两种消息传递域:点对点(Point To Point 简称P2P)、发布/订阅(Pub / Sub)
点对点的特点:
- 每个消息只能有一个消费者,注意,这里说的是每个消息,而不是每一个队列,一个队列是可以有多个消费者的
- 消息的生产者和消费者之前没有时间上的相关性,也就是说,无论消费者在生产者发送消息的时候是否是处于运行状态的,他都可以消费消息
Pub / Sub 的特点:
- 每个消息可以用多个消费者( 这里针对的还是消息 )
- 生产者和消费者之间是有时间上的相关性的。订阅一个主题的消费者只能消费到从它开始订阅之后生产者所发送的消息,而在订阅之前的消息是接收不到的。但是,JMS规范是可以允许客户端创建持久订阅的,这样消费者依然可以消费在自身未激活状态下的生产者发送的消息
在P2P中,消息的destination被称之为Queue ,而在Pub / Sub 中则被称之为Topic
JMS的消息结构
JMS消息由三部分组成:消息头、属性和消息体
JMS消息头
JMS Destination
消息发送的目的地,主要就是Queue或者Topic,在创建队列或者topic的时候会自动分配
JMS DeliveryMode
消息的传递模式,分为两种:持久模式和非持久模式,一条持久的消息只能被传送一次且仅有一次,当中间件发生故障的时候,这条消息并不会丢失,会在服务器恢复之后继续传递;一条非持久模式的消息最多会传递一次,当服务器发生故障的时候,这条消息会发生丢失
JMS Expiration
消息的过期时间,等于发送消息时设置的timeToLive值加上发送时刻的GMT时间值,如果 timeToLive的值为0,则表示该消息永不过期,直到等到消费者来消费。如果在设置了过期时间后,消息在指定的过期时间内没有发送到目的地,那么该条消息将会被清除,默认是0
JMSPriority
消息的优先级,有十个优先级,0–4表示普通消息,5–9表示加急消息,JMS不要求中间件一定要按照这十个优先级来发送消息,但是要保证加急消息一定要在普通消息到达之前到达,默认值是4
JMS MessageID
消息的标识ID,由JMS Provider产生,自动分配
JMS ReplyTo
提供本消息的回复消息的地址,由开发者设置,通过这个设置,可以在消费者消费消息之后给出一个应答,也就是在消息投递之后消费者消费的反馈
JMS Redelivered
消息的重新投递,如果一个客户端收到了消息设置了JMS Redelivered的属性,则表示之前可能已经接收过该消息,但是由于某种原因没有确认(acknowledge),如果该消息被重新投递,那么这个属性的值为true,反之则为false
JMS消息属性
由应用程序设置和添加的属性,比如:message.setStringProperty( “key”, “value”);
使用JMS定义的属性,使用JMSX作为消息的前缀,如:JMSXUserID:发送消息的用户标识,发送时提供商设置,JMSXAppID: 发送消息时的应用ID,发送时由提供商设置等等
- JMS供应商特定的属性
JMS的可靠性机制
消息接收确认
一个消息只有被确认之后,才被认为是成功消费了,消息的成功消费通常需要包含三个阶段:客户端接收消息、处理消息和消息确认
在事务性会话中,当一个事务提交后,消息确认自动发生
1
2
3
4// 事务性会话,第一个参数为true
Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
// 事务提交,消息确认自动发生
session.commit();在非事务性会话中,消息何时被确认取决于创建会话时的应答模式,有三种应答模式:
- Session.AUTO_ACKNOWLEDGE:当客户端成功的从receive( )方法返回或者从MessageListener.onMessage方法成功返回的时候,消息会被自动确认(一般使用这种模式)
- Session.CLIENT_ACKNOWLEDGE:客户端通过调用消息的acknowledge方法确认,需要注意的是,在使用这种模式时,确认是在会话层上进行的,也就是说,在确认了一条消息的时候,会将之前所有已经消费过的消息一同确认。例如,已经消费了10条消息,在对第5条消息调用acknowledge方法进行确认的时候,这10条消息都会被确认
- Session.DUP_ACKNOWLEDGE:该选择只是会话迟钝的确认消息的提交
消息的持久性
JMS支持两种消息提交模式,一种是持久的( PERSISTENT ),另外一种则是非持久性的(NON_PERSISTENT),持久性的消息不会因为JMS Provider的故障而丢失。这意味着持久性消息传送至目标时,消息服务会将其放入持久性数据存储,这会增加消息传送的开销,显然效率时候没有非持久性的高的
消息的优先级
这个在之前有提过,共有10个级别,默认的级别是4,加急消息一定会先与普通消息到达,需要注意的是,JMS Provider不一定会按照消息的优先级提交消息(那如何保证加急消息一定先到达?)
消息过期
可以设置消息的过期时间,默认是永不过期
消息的临时目的地
1
2
3
4
5
6
7
8// 生产者创建临时queue
TemporaryQueue temporaryQueue = session.createTemporaryQueue();
// 生产者将临时queue作为消息消费之后的应答 destination
message.setJMSReplyTo(temporaryQueue);
// 消息者从消息中获取应答 destination
Destination replyTo = message.getJMSReplyTo(); 临时目的地的存在时间只保持在创建它们的连接所保持的时间,只有创建了该临时目的地的连接上的消费者才能从这个队列中获取消息
JMS持久订阅
持久订阅必须使用PERSISTENT提交消息,创建持久订阅的第一个参数必须是一个topic,JMS Provider会存储发布到持久订阅上的消息。如果最初创建持久订阅的客户或其他客户,使用相同的连接工厂和连接的客户ID,相同的topic,相同的订阅名,那么再次调用会话上的创建持久订阅的方法时,该持久订阅就会被激活,JMS Provider会向客户端发送客户端处于非激活状态时的消息。
持久订阅在某一时刻只能有一个订阅者,在被创建之后会一直保留,在会话调用unsubscribe( )方法时失效
本地事务
JMS Session提供了commit( )和rollback( )方法来提交和回滚事务,事务的提交意味着生产的所有消息都将被提交,消费的所有消息都将被确认;回滚则生产的所有消息被销毁,消费的所有消息被恢复并重新提交,除非已经失效的消息,生产者和消费者的事务不能包含在同一个事务中
JMS的PTP模型
定义了客户端如何向队列发送消息,从队列接收消息,以及浏览队列中的消息,PTP模型是基于队列的,可以包含各中消息
特点
如果在Session关闭时,有些消息已经被接收但是还没有被确认,那么当消费者下次再连接到队列时,这些消息不会被再次接收
如果在receive方法中设定了消息选择条件,那么不符合条件的消息将会留在队列中,不会被接收到
队列可以长久的保存消息直到消费者消费(没有设置过期条件的情况下),因此消费者不需要因为担心丢失消息而时刻和队列保持连接状态,这也提现出了异步传输的优势
JMS的Pub/Sub模型
定义了如何向内容节点发布和订阅消息,这些节点比称作topic,发布者发布消息到主题,订阅者从主题中订阅消息