spring rabbitmq 消息确认机制和事务支持

来源:互联网 发布:怎样做网络宣传 编辑:程序博客网 时间:2024/06/05 03:04

确认并且保证消息被送达,提供了两种方式:发布确认和事务。(两者不可同时使用)在channel为事务时,不可引入确认模式;同样channel为确认模式下,不可使用事务。

发布确认:
Confirms给客户端一种轻量级的方式,能够跟踪哪些消息被broker处理,哪些可能因为broker宕掉或者网络失败的情况而重新发布。
确认消息是否到达broker服务器,也就是只确认是否正确到达exchange中即可,只要正确的到达exchange中,broker即可确认该消息返回给客户端ack。
有两种方式:消息的确认和消息发送失败的回调。
消息确认:
在配置connectionFactory 中启用消息确认:

    <bean id="connectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">          <property name="host" value="${rabbitmq.host}" />          <property name="port" value="${rabbitmq.port}" />          <property name="username" value="${rabbitmq.username}" />          <property name="password" value="${rabbitmq.password}" />          <property name="virtualHost" value="${rabbitmq.virtualHost}" />        <!-- 缓存模式   CONNECTION CHANNEL,默认的缓存模式是CHANNEL。        当缓存模式是 CONNECTION时, 队列的自动声明等等 (参考 the section called “Automatic Declaration of Exchanges, Queues and Bindings”) 将不再支持。        在框架(如. RabbitTemplate) 中使用的通道将会可靠地返回到缓存中.如果在框架外创建了通道 (如.直接访问connection(s)并调用 createChannel() ),        你必须可靠地返回它们(通过关闭),也许需要在 finally 块中以防止耗尽通道.         -->        <property name="cacheMode" value="CHANNEL"/>        <!-- 默认通道缓存25,多线程环境中,较小的缓存意味着通道的创建和关闭将以很高的速率运行.加大默认缓存大小可避免这种开销         如果达到了限制,调用线程将会阻塞,直到某个通道可用或者超时, 在后者的情况中,将抛出 AmqpTimeoutException异常.-->        <property name="channelCacheSize" value="10"/>        <!-- channelCheckoutTimeout属性. 当此属性的值大于0时, channelCacheSize会变成连接上创建通道数目的限制. -->        <property name="channelCheckoutTimeout" value="200"/>        <!-- 发布确认必须配置在CachingConnectionFactory上 -->        <property name="publisherConfirms" value="true"/>    </bean>
模板配置消息确认回调方法:

    <bean id="confirmCallback" class="com.convict.rabbitmq.spring.sync.callback.MsgSendConfirmCallBack"/>    <!-- 同步访问rabbitmq-->    <bean id="rabbitTemplate"  class="org.springframework.amqp.rabbit.core.RabbitTemplate">          <constructor-arg name="connectionFactory" ref="connectionFactory"/>        <property name="exchange" value="${rabbitmq.exchangeName}"/>        <!-- 也可以在发送的时候手动设置routing key -->        <property name="routingKey" value="${rabbitmq.msg.routing}"/>        <!--消息确认回调 -->        <property name="confirmCallback" ref="confirmCallback"/>    </bean>

MsgSendConfirmCallBack 代码如下:

    public class MsgSendConfirmCallBack implements RabbitTemplate.ConfirmCallback {        @Override        public void confirm(CorrelationData correlationData, boolean ack, String cause) {            if (ack) {                System.out.println("消息确认成功");            } else {                //处理丢失的消息                System.out.println("消息确认失败,"+cause);            }        }    }


消息发送失败回调:

CachingConnectionFactory配置中启用回调
<property name="publisherReturns" value="true"/>
定义回调方法,并在消息模板中指定

<bean id="sendReturnCallback" class="com.convict.rabbitmq.spring.sync.callback.MsgSendReturnCallback"/><bean id="rabbitTemplate"  class="org.springframework.amqp.rabbit.core.RabbitTemplate">          <constructor-arg name="connectionFactory" ref="connectionFactory"/>        <property name="exchange" value="${rabbitmq.exchangeName}"/>        <!-- 也可以在发送的时候手动设置routing key -->        <property name="routingKey" value="${rabbitmq.msg.routing}"/>        <property name="returnCallback" ref="sendReturnCallback"/>    </bean>
MsgSendReturnCallback 方法如下:
public class MsgSendReturnCallback implements RabbitTemplate.ReturnCallback {@Overridepublic void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {String msgJson  = new String(message.getBody());System.out.println("Returned Message:"+msgJson);}}

事务
Spring AMQP做的不仅仅是回滚事务,而且可以手动拒绝消息,如当监听容器发生异常时是否重新入队。
持久化的消息是应该在broker重启前都有效。如果在消息有机会写入到磁盘之前broker宕掉,消息仍然会丢失。在某些情况下,这是不够的,发布者需要知道消息是否处理正确。简单的解决方案是使用事务,即提交每条消息。

使用事务有两个问题:
一、会阻塞,发布者必须等待broker处理每个消息。如果发布者知道在broker死掉之前哪些消息没有被处理就足够了。
二、事务是重量级的,每次提交都需要fsync(),需要耗费大量的时间。
confirm模式下,broker将会确认消息并处理。这种模式下是异步的,生产者可以流水式的发布而不用等待broker,broker可以批量的往磁盘写入。

场景:业务处理伴随消息的发送,业务处理失败(事物回滚)后要求消息不发送。rabbitmq 使用调用者的外部事物。
示例:使用spring事物,来处理业务并发送消息,并且把消息记录到数据库中防止消息的丢失。
代码说明:
1、rabbitTemplate 使用外部事物配置
    <!-- 同步访问rabbitmq-->    <bean id="rabbitTemplate"  class="org.springframework.amqp.rabbit.core.RabbitTemplate">          <constructor-arg name="connectionFactory" ref="connectionFactory"/>        <property name="exchange" value="${rabbitmq.exchangeName}"/>        <!-- 也可以在发送的时候手动设置routing key -->        <property name="routingKey" value="${rabbitmq.msg.routing}"/>        <!-- 使用外部事物 -->        <property name="channelTransacted" value="true"/>    </bean>
2、创建消息表 CREATE TABLE tb_msg(id BIGINT,tag INT,txt TEXT); 使用 spring-mybatis 搭建表的操作,配置事物。
3、模拟 MsgService 中业务处理方法如下(事物切面在service层):
public void addMsg(Msg msg) throws Exception {        //模拟业务操作        Thread.sleep(1000);        //消息保存到数据库,和业务操作为同一个事物        msgDao.insert(msg);        //发送消息        rabbitTemplate.convertAndSend(msg.getTxt());        if (msg.getId() == null || msg.getId() % 2 == 0) {//事物,偶数的回滚            throw new Exception();        }    }

当事物回滚,消息也不会发送。

注意:一个具有事务的channel不能放入到确认模式,同样确认模式下的channel不能用事务。

完整code:http://download.csdn.net/detail/convict_eva/9625428

参考:http://my.oschina.net/lzhaoqiang/blog/670749

0 0
原创粉丝点击