Web | RabbitMQ | 高级应用

一、消息可靠性投递

为了保证消息生产者 ----》队列是可靠的,RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。

  1. confirm 确认模式:监听从消费者 ----》交换机
  2. return 退回模式:监听从交换机 ----》队列

在这里插入图片描述

1. confirm 确认模式

confirm确认模式 是监听从消费者到交换机之间的过程的,成功/失败都会执行相应的回调方法,只是回调消息不同,执行逻辑可以自定义。

1.1 整合Springboot/Spring后中的实现

主要是通过ConnectionFactory开启和RabbitTemplate设置回调逻辑

第一步:开启

spring xml文件中配置

对比springboot多了这一步:<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
在定义connectionFactory时设置其参数publisher-confirms="true",并没有找到其具体实现类是哪个,只是看到ConnectionFactory接口中的默认方法可以获取这个参数,但没看到设置的API方法。

<rabbit:connection-factory id="connectionFactory" 
						   host="${rabbitmq.host}" 
						   publisher-confirms="true"
                           port="${rabbitmq.port}"
                           username="${rabbitmq.username}"
                           password="${rabbitmq.password}"
                           virtual-host="${rabbitmq.virtual-host}"/>

<!--定义rabbitTemplate对象操作可以在代码中方便发送消息-->
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>

springboot yaml文件中配置

spring.rabbitmq.publisher-confirms: true #老版本的写法
spring.rabbitmq.publisher-confirm-type: correlated #新版本的写法

ConnectionFactory配置类中指定

spring\springboot都可用

CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
//开启确认模式
connectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);
//开启退回模式
connectionFactory.setPublisherReturns(true);
第二步:设置回调函数

注意:这个回调逻辑时子线程在执行,所以需要注意共享变量的安全,以及线程的顺序执行。另外每个rabbitTemplate只能设置一个回调,也就是说每个rabbitTemplate对象设置了ConfirmCallback后不能再次设置,可以考虑实现ConfirmCallback,并用单例。

spring/springboot

//spring中使用rabbitTemplate要设置相应connectionFactory属性,springboot中好像不用
@Autowired
private RabbitTemplate rabbitTemplate;

rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * 发送消息后此方法就会被执行
             * @param correlationData :消息相关配置信息
             * @param b :交换机是否接收到消息
             * @param s :失败原因,成功为null
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                if(b){
                    System.out.println("confirm方法被执行,消息发送成功");
                }else {
                    System.out.println("confirm方法被执行,消息发送失败");
                    System.out.println("错误信息: "+s);
                    //可以自己定义一些逻辑,让消息再次发送
                }
            }
        });
        //rabbitTemplate也可以发送信息,至于和channel有什么区别,没研究到
rabbitTemplate.convertAndSend("test_exchange_confirm11","confirm","message_confirm");       

1.2 Channel类

第一步:开启

在 channel上开启确认模式: channel.confirmSelect();

第二步:设置回调函数
channel.addConfirmListener(new ConfirmListener() {
            /**
             * 返回成功的回调函数
             */
            public void handleAck(long deliveryTag, boolean multiple) throws IOException {
                System.out.println("succuss ack");
                System.out.println(multiple);
                System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
            }
            /**
             * 返回失败的回调函数
             */
            public void handleNack(long deliveryTag, boolean multiple) throws IOException {
                System.out.printf("defeat ack");
                System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");
            }
        });

1.3 总结

其实就两类方法一个是基于Channel,一个是基于RabbitTemplate,前者设置开启和回调函数都是Channel,后者开启设置使用了ConnectionFactory。开启了confirm模式后,不管成功还是失败都会执行callback,只是返回的消息不同而已。

2. Return 退回模式

当消息发送给Exchange后,Exchange路由到Queue失败时才会执行 ReturnCallBack,这里只说整合Springboot/Spring后中的实现(用RabbitTemple),和channel.basicpublish()中的mandatory参数设置为true很差不多

第一步:开启

spring:

<rabbit:connection-factory id="connectionFactory" 
						   host="${rabbitmq.host}" 
						   publisher-confirms="true"
						   publisher-returns="true"
                           port="${rabbitmq.port}"
                           username="${rabbitmq.username}"
                           password="${rabbitmq.password}"
                           virtual-host="${rabbitmq.virtual-host}"/>

<!--定义rabbitTemplate对象操作可以在代码中方便发送消息-->
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>

springboot:

#开启 confirm 确认机制
spring.rabbitmq.publisher-confirms: true
#开启 return 确认机制
spring.rabbitmq.publisher-returns: true
#设置为 true 后 消费者在消息没有被路由到合适队列情况下会被return监听,而不会自动删除
spring.rabbitmq.template.mandatory: true
第二步:设置回调函数
//设置交换机处理失败消息的模式
//默认false,如果消息没有路由到Queue,则丢弃消息(默认),回调函数不会被执行。
//设置为true,如果消息没有路由到Queue,则会将消息退回给producer,并执行ReturnCallBack
//在springbootyaml参数中设置过了这里可以不设置,不知道spring中是不是可以配置文件中直接注入
        rabbitTemplate.setMandatory(true);

        //2.设置ReturnCallBack
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             *
             * @param message   消息对象
             * @param replyCode 错误码
             * @param replyText 错误信息
             * @param exchange  交换机
             * @param routingKey 路由键
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("return 执行了....");

                System.out.println(message);
                System.out.println(replyCode);
                System.out.println(replyText);
                System.out.println(exchange);
                System.out.println(routingKey);

                //处理,会让交换机将消息给其它队列
            }
        });


        //3. 发送消息
        rabbitTemplate.convertAndSend("test_exchange_confirm", "confirm", "message confirm....");

小结

使用rabbitTemplate.setReturnCallback设置退回函数,当消息从exchange路由到queue失败后,如果开启了退回模式且设置了rabbitTemplate.setMandatory(true)参数,则会将消息退回给producer,并执行回调函数returnedMessage。否则会别丢弃则会丢弃消息。

疑问:
如果是这样的话,publisher-returns: true这个参数还有什么意义,直接设置Mandatory不就得了吗?

并不清楚和channel.basicPublish()中设置mandatory参数为true或false的区别在哪,这个只设置mandatory可以实现吗?

3. 对比

监听过程callback执行条件失败怎么处理配置位置
confirm模式publisher → Exchange开启confirm模式;
不管成功还是失败都会执行
callback会带回成功和失败的信息,接下来取决于自定义的callback逻辑,一般会重新发送转发什么的ConnectionFactory,也就是针对整个Virtual Host的连接
return模式Exchange → Queue1. 开启return模式
2. 指定mandatory为true
3. 出现错误
callback会带回失败的信息,接下来取决于自定义的callback逻辑 ,一般会重新发送,转发什么的开启是在ConnectionFactory,设置mandatory是在通道中

二、消费端确认

消费端接收到消息后通常需要确认消息,然后队列将消息删除,确认就意味着将队列中的确认的消息删除。
在监听器标签中<rabbit:listener-container>有三种确认模式:

  1. 自动确认:acknowledge=“none”:自动确认,消息发出去后直接默认消费成功直接将队列中的消息删除。
  2. 手动确认:acknowledge=“manual”:手动调用确认,具体逻辑子监听器类中。
  3. 根据异常情况确认:acknowledge=“auto”,(这种方式使用麻烦,不作讲解)

不太清楚这个和channel.basicConsume()中设置参数有什么联系,可能底层实现就是调用的channel.basicConsume()

不管是自动还是手动确认,都会执行监听类,其实与其说是监听类可以说是消费逻辑,监听器是负责接收消息对消息消费的,设置手动时会在里面自定义一些确认的逻辑。

如果设置为自动,但是监听类中逻辑里写了手动提交,貌似不会执行,未明确验证过。

如果设置为手动,失败和成功后的逻辑,以及什么时候确认消息都是自己在监听器中定义的。

设置确认方式是在监听器容器中指定的,默认为自动。

2.1 Spring

1. 创建监听容器

每个容器对应了一个Virtual Host的连接,容器指定了消息确认的方式,容器可以容纳多个监听器,每个监听器监听一个队列,消费端可以在监听器里写自己的消费逻辑和确认逻辑。
这里开启确认模式,导入监听器相当于channel.basicConsume()中指定autoAck和callback参数

<!--connection-factory:连接工厂;
	auto-declare:
	acknowledge:确认方式;
	queue-names:监听的队列的名称;
	ref:监听器
-->
<rabbit:listener-container connection-factory="connectionFactory" auto-declare="true" acknowledge="manual">
	<rabbit:listener ref="ackListener" queue-names="test_queue_confirm"/>
</rabbit:listener-container>

2. 创建监听器

手动确认的逻辑就在这里,相当于channel.basicConsume()的参数(callback)DefaultConsumer类,这里也是消费消息的逻辑和确认消息时机的逻辑所在

@Component
/**
 * 这个监听器类会在接收消息后自动执行
 * ChannelAwareMessageListener是MessageListener的子接口,
 * 由于需要Channel去调用basicNack()方法和basicAck()方法,所以需要ChannelAwareMessageListener接口
 */
public class AckListener implements ChannelAwareMessageListener{
    /**
     *
     * @param message
     * @param channel
     * @throws Exception
     */
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            System.out.println(message);
            /**
             * public void basicAck(long deliveryTag, boolean multiple)
             * deliveryTag:消息的一个标签,表示签收哪个消息
             * multiple:接收多个
             */
            int i = 1/0;
            channel.basicAck(deliveryTag,true);
        } catch (Exception e) {
            //消费失败,拒绝签收
            /**
               第一个参数:指定拒绝签收的消息的参数,在发送时会自动生成
             * 前两个参数同上
             * 第三个参数:requeue(重回队列):如果设置为ture,则消息重新回到queue,broker会重新发送消息给消费端。
             * 注:1.重新发送消息的功能并不是由开不开启手动模式决定的,开不开启只决定了是否自动提交(删除队列中的消息)
             注2.channel.basicNack方法决定是否拒绝签收,第三个参数的值决定是否重回队列,如果为false相当于拒绝签收,并丢弃消息。
             */
            channel.basicNack(deliveryTag,true,true);
        }
    }
}

2.2 springboot

1. 指定确认模式

默认自动确认,确认方式是在监听器容器中设置的,这里的监听器容器有simple、direct,不知道默认是不是simple,有什么区别不清楚。

spring.rabbitmq.listener.simple.acknowledge-mode: manual 


三、消费端限流

<rabbit:listener-container> 中配置 prefetch属性设置消费端一次拉取多少消息,确认消费后才会拉取下一次的,不确认则不会拉取下一条,所以这里不能自动确认。类比channel.basicQos()

<rabbit:listener-container connection-factory="connectionFactory" auto-declare="true" acknowledge="manual" prefetch="1">
	<rabbit:listener ref="qosListener" queue-names="test_queue_confirm"/>
</rabbit:listener-container>

监听器:

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;

/**
 * 这个监听器类会在接收消息后自动执行
 * ChannelAwareMessageListener是MessageListener的子接口,
 * 由于需要Channel去调用basicNack()方法和basicAck()方法,所以需要ChannelAwareMessageListener接口
 */
@Component
public class QosListener implements ChannelAwareMessageListener{
    /**
     *
     * @param message
     * @param channel
     * @throws Exception
     */
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();//获取消息的id
        try {
            System.out.println(message);
            channel.basicAck(deliveryTag,true);
        } catch (Exception e) {

            channel.basicNack(deliveryTag,true,true);
        }
    }
}

四、TTL过期时间设置

RabbitMQ可以对消息和整个队列(Queue)设置过期时间。

1. 规则

  1. 如果设置了消息的过期时间,也设置了队列的过期时间,它以时间短的为准。
  2. 队列过期后,会将队列所有消息全部移除。
  3. 消息过期后,但队列未过期,只有消息在队列顶端,才会判断其是否过期(移除掉),如果消息不在顶端(先添加进去队列的消息),则等到前面的消息消费完了,这条消息才会被移除而不是被消费。
  4. 如果某个设置了过期时间的队列已经建立,再去设置消息过期时,可能会报两种过期时间矛盾的错,这个时候可以删除之前的队列再去建立。

2. 设置队列过期

建立队列的属性参数,channel.queueDeclare()时也可以指定。

<!--创建queue,这里需要设置队列的属性-->
<rabbit:queue name="test_queue_ttl" id="test_queue_ttl">
	<!--设置queue的属性参数-->
	<rabbit:queue-arguments>
	   <!--
	   x-message-ttl指队列的过期时间;
	   key为队列属性名(固定写法)在官网上有或者百度;
	   value:过期时间,单位ms
	   value-type为指定value的数据类型,默认为字符串类型
	   -->
		<entry key="x-message-ttl" value="100000" value-type="java.lang.Integer"></entry>
	</rabbit:queue-arguments>
</rabbit:queue>

<!--创建topic-exchange,并绑定队列-->
<rabbit:topic-exchange name="test_exchange_ttl" >
   <rabbit:bindings>
      <rabbit:binding pattern="ttl.#" queue="test_queue_ttl"></rabbit:binding>
    </rabbit:bindings>
</rabbit:topic-exchange>

3. 设置消息过期

    @Test
    public void testTtl() {
      // 消息后处理对象,设置一些消息的参数信息
        MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {

            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //1.设置message的信息,赋予消息过期属性值
                message.getMessageProperties().setExpiration("5000");//消息的过期时间
                //2.返回该消息
                return message;
            }
        };

        //消息单独过期
        //rabbitTemplate.convertAndSend("test_exchange_ttl", "ttl.hehe", "message ttl....",messagePostProcessor);

        for (int i = 0; i < 10; i++) {
            if(i == 5){
                //消息单独过期
                rabbitTemplate.convertAndSend("test_exchange_ttl", "ttl.hehe", "message ttl....",messagePostProcessor);
            }else{
                //不过期的消息
                rabbitTemplate.convertAndSend("test_exchange_ttl", "ttl.hehe", "message ttl....");

            }

        }
    }

五、死信队列

1. 概念

  1. 当消息在一个队列中变成死信 (dead message) 之后,它能被重新publish到另一个Exchange,这个Exchange就是DLX。
  2. DLX也是一个正常的Exchange,和一般的Exchange没有区别
  3. 当这个队列中有死信时,RabbitMQ就会自动的将这个消息重新发布到设置的Exchange上去,进而被路由到另一个队列。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值