创建健壮的 jms 应用程序
jms api 提供了一下的方式来创建一个健壮的 jms 应用程序
控制消息的确认方式(acknowledgment)控制消息确认方式
如果一条消息没有被确认,那么 jms provider 会认为此消息没有成功地被消费。一条消息成功地被客户端消费通常包含3个步骤:
消息的确认是由 jms provider 触发还是由客户端触发,这取决于会话的确认模式。
在事务性会话中,事务提交的时候会自动确认消息,当事务被回滚,所有消费的消息都会被重传。
在非事务性会话中,什么时候以及如何确认消息取决于 createQueueSession 、 createTopicSession 和 createSession 方法的第二个参数的取值。有三种可选的取值:
当 QueueSession 被关闭,jmsprovider 会保持那些客户端已经接受到但还没有确认的消息,并在下一次客户端连接到队列的时候重传这些消息。同样,当TopicSession 关闭,jms provider 也会保持那些持久 TopicSubcriber 没有确认的消息。非持久TopicSubscriber 没有确认的消息,当会话关闭时会被丢弃。
如果使用队列和持久订阅,可以调用方法Session.recover使会话停止当前消息传递,并从第一个没有确认的消息开始重新传递,这会使消息传递的顺序与消息发送的顺序不一致。对于非持久订阅的TopicSubscriber,当恢复会话时 jms provider 可能会丢弃没有确认的消息。
设置消息的持久性
jms 提供了两种消息传递模式,这两种模式决定了当 jms provider 失败时消息是否会丢失。DeliveryMode 接口提供了这两种传递模式。
PERSISTENT 传递模式要求 jms provider 确保在消息传输过程中如果 jms provider 失败消息不会丢失,这是默认的传输模式。使用这种模式发送的消息在发送的时候会被记录到一个持久储存中。
NON_PERSISTENT 传递模式不要求 jms provider 把消息保存到持久存储中,同样也不保证如果 jms provider 失败,消息不会丢失。
有两种方式设置消息传递模式:
如果不指定传递模式,默认使用 PERSISTENT 模式。使用 NON_PERSISTENT 可以提高性能以及降低存储需求。
设置消息优先级
可以使用优先级使 jms provider 优先传递紧急的消息。有两种方式设置消息优先级。
消息的优先级从0(最低)到9(最高)有10种级别。如果没有设置优先级,默认优先级为4。jms provider 会确保优先级高的消息会在优先级低的消息之前被传递,但并不保证会严格按照优先级的顺序来传递消息。
允许消息过期
默认情况下,消息永远不会过期。但如果一条消息在一段时间之后就会被废弃,那么可以设置消息的过期时间。有两种方式可以设置消息的过期时间。
如果设置 timeToLive 为0,那么消息永远不会过期。
发送消息时,会使用当前时间与 timeToLive 的和设置消息的过期时间。任何在指定的过期时间之前没有被传递的消息都会被销毁。
创建临时目的
jms提供了方法 QueueSession.createTemporaryQueue 和TopicSession.createTemporaryTopic 来创建临时队列 TemporaryQueue 和临时主题TemporaryTopic,临时目的在创建它们的连接被关闭时会被销毁。
仅有使用创建临时目的的连接创建的消费者才能消费临时目的上的消息,但任何一个消息生产者都可以向临时目的上发送消息。如果关闭创建临时目的地连接,那么临时目的也会被关闭,临时目的上的消息会丢失。
创建持久订阅
为了确保发布/订阅应用程序能够接收到所有已发布的消息,在发布端可以使用 PERSISTENT 传递模式传输消息以确保消息不回在传输过程中丢失,而在订阅端则可以使用持久订阅来保证能够收到所有已发布的消息。
TopicSession.createSubscriber 方法创建一个非持久订阅者。非持久订阅者只能接收到在它处于活动状态时发布的消息。
可以使用方法 TopicSession.createDurableSubscriber 创建一个持久订阅者。持久订阅在任何时候仅能有一个订阅者。
一个持久订阅者使用一个由 jms provider维护的唯一的标识符注册一个持久订阅。随后的订阅者会恢复之前订阅者在关闭之前的状态。如果一个持久订阅没有处于活动状态的订阅者,jmsprovider 会保持订阅的消息直到这些消息被订阅者接收到或者消息过期。
使用如下设置能够创建一个持久订阅的唯一标识符:
创建了一个连接并设置了其客户 id 之后,可以使用连接创建 Session,并调用这个 Session 的createDurableSubscriber来创建一个持久订阅者,这个方法接收两个参数,第一个参数是一个主题名,第二个参数是创建的这个持久订阅的名称,例如:
调用 TopicConnection 的 start 方法后此订阅者就处于活动状态。这之后可以关闭此 TopicSubscriber:
?? ??? ???? topicSubscriber.close();
jmsprovider 会保存发布到这个主题上的消息。如果任意一个应用程序使用具有相同客户 id 的连接,并使用相同的主题和订阅名调用createDurableSubscriber 方法,那么这个持久订阅就会被激活,jms provider就会把在此订阅者处于不活动状态发布的消息传递给此订阅者。
删除一个持久订阅,首先要关闭订阅者,然后使用订阅名调用 unsubscribe 方法注销持久订阅。
?? ??? ???? topicSubscriber.close();
?? ??? ??? ??? ???topicSession.unsubscribe( "mySub" );
unsubscribe 方法会使 jms provder 删除它维护的关于订阅者的状态信息。
使用本地事务
可以把一系列操作组成一个称为事务的原子工作单元。如果一个操作失败,事务会被回滚,并可以尝试重新执行这一组操作。如果这一组操作都成功,事务则会被提交。
jms客户端可以在事务中发送和接受一组消息。jms api 中的 Session 接口提供了 commit 和 rollback方法来提交回滚事务。事务提交意味着生产的所有消息都会被发送,消费的所有消息都会被确认。事务回滚意味着生产的消息都会被销毁,消费的消息都会被重传直到它们过期。
一个事务性会话始终包含在一个事务中。只要调用了 commit 方法或 rollback 方法,这意味着一个事务的结束,新的事务的开始。关闭事务性会话会回滚正在进行的事务。
当创建会话的时候可以指定会话是否为事务性的。方法 createQueueSession 与 createTopicSession的第一个参数是一个 boolean 类型的,如果设为 true 则新建的会话为事务性的,如果为 false新建的回话则不是事务性的。这两个方法的第二个参数是设置确认模式的,这个值只有在非事务性回话中有效,事务性会话则会忽略确认模式,所以可以设置为0,例如:
?? ??? ???? topicSession = topicConnection.createTopicSession( true, 0 );
由于本地事务的提交与回滚是与回话相关联的,所以不能把对队列的主题的操作绑定到同一个事务中。因为QueueReceiver、QueueSender 与 TopicSubscriber、TopicPublisher 分别是由QueueSession 和 TopicSession 创建的。