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

确认并且保证消息被送达,提供了两种方式:发布确认和事务。(两者不可同时使用)在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 {
		@Override
		public 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

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值