消息中间件 - ActiveMQ高级特性和用法-消息的可靠性(八)

消息的可靠性

消息发送成功后,接收端接收到了消息。然后进行处理,但是可能由于某种原因,高并发也好,IO阻塞也好,反正这条消息在接收端处理失败了。而点对点的特性是一条消息,只会被一个接收端给接收,只要接收端A接收成功了,接收端B,就不可能接收到这条消息,如果是一些普通的消息还好,但是如果是一些很重要的消息,比如说用户的支付订单,用户的退款,这些与金钱相关的,是必须保证成功的,那么这个时候要怎么处理呢?必须要保证消息的可靠性,除了消息的持久化,还包括两个方面,一是生产者发送的消息可以被ActiveMQ收到,二是消费者收到了ActiveMQ发送的消息。

生产者

send方法

在生产者端,我们会使用send() 方法向ActiveMQ发送消息,默认情况下,持久化消息以同步方式发送,send() 方法会被阻塞,直到 broker 发送一个确认消息给生产者,这个确认消息表示broker已经成功接收到消息,并且持久化消息已经把消息保存到二级存储中。

实验send()方法:在模块no-spring包msgreliability下的JmsMsgReliablitySendProducer中send方法上打一个断点,可以看到send方法每执行一次,ActiveMQ管理控制台增加一条入队消息,数据库中增加一条消息。

事务消息

事务中消息(无论是否持久化),会进行异步发送,send() 方法不会被阻塞。但是commit 方法会被阻塞,直到收到确认消息,表示broker已经成功接收到消息,并且持久化消息已经把消息保存到二级存储中。

实验事务消息:在模块no-spring包msgreliability下的JmsMsgReliablityTranProducer中在session.commit()打一个断点,可以看到send方法每执行一次,ActiveMQ管理控制台和数据库中没有任何变化,只有执行完session.commit()后ActiveMQ管理控制台和数据库中才增加。

总结

非持久化又不在事务中的消息,可能会有消息的丢失。为保证消息可以被ActiveMQ收到,我们应该采用事务消息或持久化消息。

消费者

对消息的确认有4种机制

1、AUTO_ACKNOWLEDGE = 1    自动确认
2、CLIENT_ACKNOWLEDGE = 2    客户端手动确认   
3、DUPS_OK_ACKNOWLEDGE = 3    自动批量确认
4、SESSION_TRANSACTED = 0    事务提交并确认

ACK_MODE描述了Consumer与broker确认消息的方式(时机),比如当消息被Consumer接收之后,Consumer将在何时确认消息。所以ack_mode描述的不是producer于broker之间的关系,而是customer于broker之间的关系。

对于broker而言,只有接收到ACK指令,才会认为消息被正确的接收或者处理成功了,通过ACK,可以在consumer与Broker之间建立一种简单的“担保”机制.

session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

第一个参数:是否支持事务,如果为true,则会忽略第二个参数,自动被jms服务器设置为SESSION_TRANSACTED

AUTO_ACKNOWLEDGE

自动确认

    “同步”(receive)方法返回message给消息时会立即确认。

     在"异步"(messageListener)方式中,将会首先调用listener.onMessage(message),如果onMessage方法正常结束,消息将会正常确认。如果onMessage方法异常,将导致消费者要求ActiveMQ重发消息。此外需要注意,消息的重发次数是有限制的,每条消息中都会包含“redeliveryCounter”计数器,用来表示此消息已经被重发的次数,如果重发次数达到阀值,将导致broker端认为此消息无法消费,此消息将会被删除或者迁移到"dead letter"通道中。

     因此当我们使用messageListener方式消费消息时,可以在onMessage方法中使用try-catch,这样可以在处理消息出错时记录一些信息,而不是让consumer不断去重发消息;如果你没有使用try-catch,就有可能会因为异常而导致消息重复接收的问题,需要注意onMessage方法中逻辑是否能够兼容对重复消息的判断。

实验方法:在模块no-spring包msgreliability下的JmsMsgReliablityConsumerAsyn中onMessage方法中增加一条throw语句,出现消息重发的现象。

CLIENT_ACKNOWLEDGE :

客户端手动确认,这就意味着AcitveMQ将不会“自作主张”的为你ACK任何消息,开发者需要自己择机确认。可以用方法: message.acknowledge(),或session.acknowledge();效果一样。

如果忘记调用acknowledge方法,将会导致当consumer重启后,会接受到重复消息,因为对于broker而言,那些尚未真正ACK的消息被视为“未消费”。

我们可以在当前消息处理成功之后,立即调用message.acknowledge()方法来"逐个"确认消息,这样可以尽可能的减少因网络故障而导致消息重发的个数;当然也可以处理多条消息之后,间歇性的调用acknowledge方法来一次确认多条消息,减少ack的次数来提升consumer的效率,不过需要自行权衡。

实验方法:在模块no-spring包msgreliability下的JmsMsgReliablityConsumerAsyn中将session模式改为Session.CLIENT_ACKNOWLEDGE,,启动两个消费者,发送消息后,可以看到JmsMsgReliablityConsumerAsyn接收了消息但不确认。当JmsMsgReliablityConsumerAsyn重新启动后,会再一次收到同样的消息。加入message.acknowledge()后该现象消失。

DUPS_OK_ACKNOWLEDGE

类似于AUTO_ACK确认机制,为自动批量确认而生,而且具有“延迟”确认的特点,ActiveMQ会根据内部算法,在收到一定数量的消息自动进行确认。在此模式下,可能会出现重复消息,什么时候?当consumer故障重启后,那些尚未ACK的消息会重新发送过来。

SESSION_TRANSACTED

当session使用事务时,就是使用此模式。当决定事务中的消息可以确认时,必须调用session.commit()方法,commit方法将会导致当前session的事务中所有消息立即被确认。在事务开始之后的任何时机调用rollback(),意味着当前事务的结束,事务中所有的消息都将被重发。当然在commit之前抛出异常,也会导致事务的rollback。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值