消息传输在两个环节上进行:第一个环节是生产者发送消息到队列;第二个环节是消费者从队列获得消息。
因此,在下面的三个阶段可能会使消息丢失:
在生产者发送消息到代理的时候;
当代理发生故障时在代理内存中的时候;
在消费者从代理节点获取消息的时候;
由于当代理发生故障时非持久性消息总是会丢失,因此可靠传输仅适用于持久性消息。可靠传递机制可保证传输过程在上述任一阶段都不会失败。
消息的可靠性传递使用了两种机制来确保可靠:
客户端可以使用确认或事务确保消息的成功生成和消费。
代理可以持久化地存储消息,如果代理在消息被消费前发生故障,它可以检索存储的消息副本并重新消费。
以下将介绍这两种保证可靠性的机制。
消息的确认
消息中间件对消息的处理类似快递公司对快递的处理,在收件人确认收货之前,快递处于待签收状态,只有等收件人确认收货以后,才认为快递被送到了收件人手里。消息队列中的消息只有在被消费者确认之后才会被签收,才认为已经被成功的消费了,消息的成功消费通常包含三个阶段:接收消息,处理消息和消息被确认。
确认是指客户端与消息服务之间为确保可靠消息传送而发送的消息,但是对于生产者客户端和消费者客户端来说,确认的过程是不同的。
如果是生产者生成消息,消息代理将确认收到消息、将消息传送到其目的地并将其持久存储,生成方的 send() 方法会被阻塞,直到它收到代理的确认为止,这个确认过程对消息的消费者来说是透明的。
如果是消费者消费消息,客户端确认已收到从某个目的地传送来的消息并已使用它,然后消息代理从该目的地中删除该消息。
事务性会话
一个JMS客户端,可以把会话配置为事务,处理消息的发送和接收。在事务性会话中,JMS API 提供了启动(start)、提交(commit)和回滚(rollback)事务的方法。
当一个事务被提交的时候,确认自动发生,交意味着生产的所有消息被发送,消费的所有消息被确认;事务回滚意味着生产的所有消息被销毁,消费的所有消息被恢复并重新提交,除非它们已经过期。
在事务状态下进行发送操作,消息并未真正投递到中间件,而只有进行 session.commit 操作之后,消息才会发送到中间件,再转发到适当的消费者进行处理;如果是调用 rollback 操作,那么当前事务期间内所发送的消息都将取消掉。
通过在创建session的时候使用true or false来决定当前的会话是事务性还是非事务性,如下:
Session session = connection.createSession(Boolean.FALSE,Session.AUTO_ACKNOWLEDGE);
非事务性会话
在非事务性会话中,消息何时被确认取决于创建会话时的应答模式。
在
AUTO_ACKNOWLEDGE
模式下,会话自动确认客户端使用的每条消息。当客户端成功的从 receive() 方法返回的时候,或者从 MessageListener.onMessage() 方法成功返回的时候,会话自动确认客户收到的消息。会话线程会被阻塞,以等待代理确认它已处理了每个已使用消息的客户端的确认。在
CLIENT_ACKNOWLEDGE
模式下,在一条或多条消息被使用后,客户端通过调用消息对象的 acknowledge() 方法来显式确认。这样该会话就确认了自上次调用该方法后使用的所有消息。在这种模式中,确认是在会话层上进行,确认一个被消费的消息将自动确认所有已被会话消费的消息。会话线程会被阻塞,以等待代理确认它已处理了客户端确认。在
DUPS_OK_ACKNOWLEDGE
模式下,消息延迟确认,虽然该模式可保证不会丢失消息,但并不能保证不会收到重复的消息。会话线程不会因等待代理确认而被阻塞,因为在该模式下代理确认不是必需的。
消息的持久化存储
消息的持久化存储是保证可靠性最重要的机制,JMS支持以下两种消息提交模式:
PERSISTENT:表示 JMS provider持久保存消息,以保证消息不会因为JMS provider的失败而丢失
NON_PERSISTENT:表示 JMS provider 不持久保存消息
消息发送端可通过下面的代码来设置持久化和非持久化的消息提交模式:
MessageProducer producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
对于非持久化的消息,JMS provider 不会将它存到文件或者其他存储中,也就是说非持久化消息在内存中,如果JMS provider 宕机了,那么内存中的非持久化消息就会丢失。
对于持久化消息,消息提供者会使用存储转发机制,先将消息存储到文件或其他存储中,等消息发送成功后再删除。如果 JMS provider 宕机了,那么这些未送达的消息不会丢失;待JMS provider 恢复正常后,会重新读取这些消息,并发送给对应的消费者。