JMS的消息类型
1)StreamMessage:这种类型的消息内容由序列化的对象流组成,读取消息时必须按写对象时的顺序从流中读取对象。
2)MapMessage:这种类型的消息内容由多个key-value对组成,要求key是唯一的。
3)TextMessage:这种类型的消息内容就是普通字符串。
4)ObjectMessage:该消息内容包含序列化的对象。
5)BytesMessage:该消息的内容是原始的字节流。只有当客户端需要完全控制原始消息格式时才考虑使用这种消息类型。
6)WebLogic还在TextMessage的基础上进行了扩展,扩展出了XMLMessage类型,这种类型的消息对XML消息内容的传递和选择提供了优化。
不管是那种消息对象,都是Message接口的实例。对于一个Message对象,包括
JMS消息中的消息头、消息属性本质上都是一系列key-value对,用于对消息的描述,可以成为消息的元数据。
消息头和消息属性的区别在于:消息头的所有key是标准的、固定的,而且Message接口为这些标准提供了相应的setter和getter方法。单JMS属性则允许开发者任意定义。
Message接口中包含了添加属性的方法:setXxxProperty(String name,xxx value)。
例如,msg.setStringProperty(“ConType”,”text”);
消费者可以通过getXXXProperty()来读取这些属性值。
当客户需要发送两个消息时,创建一个Message对象,通过修改Message对象的内容,sender可以重用发送两条消息。
对于JMS消费者,JMS消息时只读的。
sender.setDeliveryMode(DeliveryMode.PERSISTENT);
sender.setTimeToLive(20000);
上面用于修改消息的传递模式、有效时间。
NON_PERSISTENT:指定消息是一个无需持久化保存的消息。
PERSISTENT:指定消息是一个需要持久化报错的消息。
也可以在调用send方法时设置消息的传递模式或有效时间。
send(Message message,int deliveryMode,int priority,long timeToLive)
当我们将一个消息设置为需要持久化,JMS服务器会在发送之前先保存到数据库或文件中,保存完成后才会发送,因此即使系统崩溃,消息内容也不会丢失。
JMS允许生产者手动设置JMS消息的优先级,一共支持0~9个优先级,0~4是普通的优先级,5~9是较高的优先级。如果生产者没有修改JMS的优先级,那么使用默认优先级是4。
JMS允许两种方式改变优先级
1,设置消息生产者的默认优先级。
2,当调用send方法时指定该消息的优先级。
sender.setPriority(9);//设置默认的优先级
如果在send方法中指定了优先级,则对于单个消息,默认优先级设置被覆盖。
JMS会一直保持着每个消息,直到消费者送回一个确认信号为止。
对于事务性的JMS,当一个事务对收到的消息进行了适当的处理,且该事务被提交之后将会反馈一个确认消息给生产者。
对于非事务性会话而言,则可以在创建JMS会话时指定消费者使用的确认方式。
Session session=conn.createSession(false/*不是事务性会话*/,
Session.AUTO_ACKNOWLEDGE);
在Session接口中定义了如下几种确认方式。
1)AUTO_ACKNOWLEDGE:自动确认方式。对于同步消费者,当消费者的receive方法调用完成且没有异常发生的,系统将会自动对收到的消息进行确认;对于异步消费者,当onMessage执行完成且没有异常发生时,将会自动对收到的消息进行确认。
2)CLIENT_ACKNOWLEDGE:客户端确认方式。要求开发者使用javax.jms.Message的acknowledge方法来进行显式确认。当然并不是要求对每个消息都进行确认;相反,只要调用了acknowledge方法,将确认当前和以前收到的任何消息。
3)DUPS_OK_ACKNOWLEDGE:延迟确认方式。并不要求JMS消费者立即确认收到的消息,因此大多数情况下比AUTO_ACKNOWLEDGE的性能更好。但从另一个方面来看,当系统崩溃或网络故障时,由于JMS消费者没有立即对消息确认,JMS生产者可以重发那些已经收到的消息。
WebLogic还提供了两种确认方式
4)NO_ACKNOWLEDGE:无需确认方式,不要求消费者对收到的消息进行确认。在这种情况下,JMS服务器把消息传递给消费者之后将不再保存这些消息。
5)MULTICAST_NO_ACKNOWLEDGE:无需确认方式,主要针对通过IP广播方式把消息传递给主题订阅者的情况。
由于消费者可能只对某一部分消息感兴趣,消费者希望JMS服务器只将感兴趣的消息传递过来,因此JMS提供了消息选择器机制。消息选择器本质属于消息过滤。
消息选择器是根据消息头或消息属性来进行过滤的,对消息体的内容不做任何考虑。JMS消费者需要提供消息过滤的表达式,表达式返回true时,该消息才会被传递给消费者。JMS消息选择器的表达式是一个SQL-92的字符串表达式,该表达式由JMS消息头、消息属性和SQL-92比较运算符、逻辑运算符组成,该表达式必须返回一个boolean值。
消费者可以指定消息选择器,可以在创建消费者时指定。
createConsumer(Destination destination,String messageSelector):第二个参数用于为该消费者指定消息选择器。
MessageConsumer receiver=session.createConsumer(dest,”JMSPriority > 5”);
MessageConsumer receiver=session.createConsumer(dest,”ConType = ‘TXT’”);
一般JMS消息目的是在应用服务器配置管理的,消息目的被绑定到JNDI命名,即使服务器重启这些JMS消息目的也会一直存在,也可以允许客户端使用临时创建的消息目的,爱客户端退出后删除。
TemporaryQueue createTemporaryQueue():创建一个临时的消息队列
TemporaryTopic createTemporaryTopic():创建一个临时的消息主题
TemporaryQueue是Queue的子类,完全可作为普通Queue使用,增加了一个delete方法,用于删除自身释放资源。TemporaryTopic则是Topic的子类,增加了一个delete方法。
关于JMS的临时目的,有3点说明:
1)客户端不应该视图在临时目的的基础上创建可靠的消息订阅
2)每个临时目的只能依附于单个的JMS连接,且只能以该JMS连接来创建临时目的的JMS消费者。
3)因为临时目的会随着服务器的重启而自动消失,因此不要希望临时目的会持久化地保存消息。任何发送到临时目的的持久化消息都会被JMS服务器自动转换为NON_PRESISTENT传递模式。
class MessageSender{
TemporaryQueue tempDestionation=session.createTemporaryQueue();
Sysytem.out.println(tempDestionation);
msg.setJMSRelpyTo(tempDestionation);
sender.send(msg);
Thread.sleep(5000);
QueueBrowser browser=session.createBrowser((javax.jms.Queue)tempDestination);
//获取消息队列中的所有消息
Enumeration em=browser.getEnumeration();
while(em.hasMoreElements()){
TextMessage acMsg=(TextMessage)em.nextElement();
if(acMsg.getText().equals(HAS_RECEIVED)){
System.out.println(“对方已经收到消息”);
break
}
}
//临时目的调用delete方法删除自己。 tempDestination.delete();
}
class Receiver{ MessageConsumer receiver=session.createConsumer(dest);
TextMessage msg=(TextMessage)receiver.receive();
System.out.println(msg.getText());
Destination replyTo=msg.getJMSReplyTo();
MessageProducer sender=session.createProducer(replyTo);
TextMessage replyMsg=session.createTextMessage();
replyMsg.setText(HAD_RECEIVED);
sender.send(replyMsg);
}
JMS还提供一个QueueBrowser接口,用于浏览指定消息队列中的所有消息。
Session提供了两个方法来创建队列浏览器
createBrowser(Queue queue):根据指定的队列来创建消息浏览器
createBrowser(Queue queue,String messageSelector):第二个参数指定消息选择器。
QueueBrowser browser=session.createBrowser((javax.jms.Queue)dest);
Enumeration em=browser.getEnumeration();
while(em.hasMoreElements()){
TextMessage msg=(TextMessage)em.nextElement();
System.out.println(msg.getText());
}
JMS并未提供API来浏览主题中的消息,这是因为发布到主题的消息在他们到达主题之后就会立即消失,虽然可靠的消息会保留,但是JMS未提供API。
虽然JMS的消息确认机制提供了一定程度的可靠性,但是对于强健的企业仍然需要为JMS增加事务控制。
目前提供了两种事务控制方式
1,使用事务性的Session
2,在JTA全局事务中使用JMS
使用事务性Session
createSession(boolean transacted,int acknowledgeMode):第一个参数用于指定是否是事务性Session。如果第一个参数为true时,第二个参数的值将会被忽略。
一旦获取了事务性的Session之后,可以通过session提供的commit和rollback方法来显示的提交或回滚事务。
对于生产者,使用事务性Session时,所发送的消息先会被缓存,在事务提交之前消费者不会接收到任何未提交的消息。提交后事务后,之前所有发送的消息才会被整体性地传递到消费者,如果回滚,JMS服务器将直接丢弃所有缓存的消息。
对于消费者,使用事务性Session时,可以让程序来控制消息的确认方式,正如使用CLIENT_ACKNOWLEDGE确认方式一样。消费者提交事务才会向生产者确认之前收到的消息。如果事务回滚,JMS服务器会将所有消息退还给相应的消息队列和消息主题。
在单个JMS会话期间,如果需要使用事务性的操作,则应该使用事务性会话,但是其他资源如数据库、EJB操作等则不会参与事务性会话的事务。如果需要参与,则应该考虑使用JTA事务。
class MessageSender{
UserTransaction tx=(UserTransaction)ctx.lookup(“javax.transaction.UserTranscation”);
tx.begin();
......
tx.commit();
}
为了让JMS客户端可以接收到JMS服务器上的异常信息,JMS提供了一种异常监听机制,JMS的异常监听器需要实现javax.jms.ExceptionListener接口。
onException(JMSException exception):当JMS服务器上出现异常时,JMS服务器上异常监听器的该方法将会被触发。
调用sned方法引发的异常不会触发上面的方法。
ConnectionFactory connFactory=(ConnectionFactory)ctx.lookup(“...”);
Connection conn=connFactory.createConnection();
conn.setExceptionListener(new ExceptionListener{
public void onException(javax.jms.JMSException ex){
...
}
}
);