RabbitMQ(四)死信队列和延迟队列

在定义业务队列的时候,要考虑指定一个死信交换机,死信交换机可以和任何一个普通的队列进行绑定,实际上就是设置某个队列的属性,然后在业务队列出现死信的时候就会将数据发送到死信队列。

进入死信队列的情况:

  • 消息被拒绝(basic.reject/ basic.nack)并且不再重新投递 requeue=false
  • 消息超期 (rabbitmq Time-To-Live -> messageProperties.setExpiration())
  • 队列超载。比如,发送邮件队列的长度已满

变成了 “死信” 后 被重新投递(publish)到另一个Exchange 该Exchange 就是DLX
然后该Exchange 根据绑定规则 转发到对应的 队列上 ,监听该队列 就可以重新消费。
在这里插入图片描述
配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/rabbit
        http://www.springframework.org/schema/rabbit/spring-rabbit-1.6.xsd">
        <!--配置connection-factory,指定连接rabbit server参数 -->
        <rabbit:connection-factory id="rabbitConnectionFactory" username="${rabbit_username}" password="${rabbit_password}"
        host="${rabbit_host}" port="${rabbit_port}" publisher-confirms="true"/>

        <!--定义rabbit template用于数据的接收和发送 -->
        <rabbit:template id="rabbitTemplate"  connection-factory="rabbitConnectionFactory"exchange="exchangeTest"
        confirm-callback="publishService" mandatory="true"/>

        <!--通过指定下面的admin信息,当前producer中的exchange和queue会在rabbitmq服务器上自动生成 -->
        <rabbit:admin connection-factory="rabbitConnectionFactory" />

        <!--定义queue -->
        <rabbit:queue name="queueTest" durable="true" auto-delete="false" exclusive="false" >
            <rabbit:queue-arguments>
                <entry key="x-dead-letter-exchange" value="deadExchange" />
                <entry key="x-dead-letter-routing-key" value="queueTestKey" />
            </rabbit:queue-arguments>
        </rabbit:queue>
        <!-- 定义direct exchange,绑定queueTest -->
        <rabbit:direct-exchange name="exchangeTest" durable="true" auto-delete="false">
                <rabbit:bindings>
                         <rabbit:binding queue="queueTest"  key="queueTestKey" > </rabbit:binding>
                 </rabbit:bindings>
        </rabbit:direct-exchange>

    <!--定义死信队列 -->
    <rabbit:queue name="deadQueue" durable="true" auto-declare="false" exclusive="false" />
    <!-- 定义死信exchange -->
    <rabbit:direct-exchange name="deadExchange" durable="true" auto-delete="false" >
        <rabbit:bindings>
            <rabbit:binding queue="deadQueue" key="queueTestKey"> </rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>

        <!-- 消息接收者 -->
        <bean id="confirmListener" class="com.example.hello2.controller.ConfirmListener"></bean>
        <!-- 死信消息接收者 -->
        <bean id="deadQueueListener" class="com.example.hello2.controller.DeadQueueListener"></bean>

        <rabbit:listener-container connection-factory="rabbitConnectionFactory" acknowledge="manual" prefetch="3" >
            <rabbit:listener queues="queueTest" ref="confirmListener" />
            <rabbit:listener queues="deadQueue" ref="deadQueueListener"/>
        </rabbit:listener-container>

</beans>

此时队列情况:
在这里插入图片描述
正常消费者:

@Override
public void onMessage(Message message, Channel channel) throws Exception {
    try {
        String messageStr = new String(message.getBody());
        System.out.println("消费者接收到信息 : " + messageStr);
        if (messageStr.equals("hello")) {
            //告诉服务器收到这条消息 已经被我消费了 可以在队列删掉 这样以后就不会再发了 否则消息服务器以为这条消息没处理掉 后续还会在发
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }else{
            channel.basicNack(message.getMessageProperties().getDeliveryTag(),false, false);
        }
    } catch (Exception e) {
        e.printStackTrace();//TODO 业务处理
        //丢弃这条消息
        channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
    }
}

死信消费者:

@Service("deadQueueListener")
public class DeadQueueListener implements ChannelAwareMessageListener {
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        try {
            String messageStr = new String(message.getBody());
            System.out.println("【死信队列】——消费者接收到信息 : " + messageStr);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            e.printStackTrace();//TODO 业务处理
            //丢弃这条消息
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
        }
    }
}

测试:

http://localhost:8081/test/send?exchange=exchangeTest&key=queueTestKey&message=helloaaa

在这里插入图片描述

延迟队列

场景:三方支付,扫码支付调用上游的扫码接口,当扫码有效期过后去调用查询接口查询结果。实现方式:每当一笔扫码支付请求后,立即将此订单号放入延迟队列中,队列过期时间为二维码有效期,此队列没有设置消费者,过了有效期后消息会重新路由到指定的的队列,有消费者去执行订单查询。
RabbitMQ本身没有直接支持延迟队列功能,但是可以通过以下特性模拟出延迟队列的功能。 但是我们可以通过RabbitMQ的两个特性来曲线实现延迟队列:Time To Live(TTL) 和 Dead Letter Exchanges(DLX)
RabbitMQ可以针对Queue和Message设置 x-message-tt,来控制消息的生存时间,如果超时,则消息变为dead letter(死信)。
配置文件:

<!--定义queue -->
<rabbit:queue name="queueTest" durable="true" auto-delete="false" exclusive="false" >
    <rabbit:queue-arguments>
        <entry key="x-dead-letter-exchange" value="deadExchange" />
        <entry key="x-dead-letter-routing-key" value="queueTestKey" />
        <entry key="x-message-ttl">
            <value type="java.lang.Long">10000</value>
        </entry>
    </rabbit:queue-arguments>
</rabbit:queue>

并且把消费者去掉

<rabbit:listener-container connection-factory="rabbitConnectionFactory" acknowledge="manual" prefetch="3" >
    <!--<rabbit:listener queues="queueTest" ref="confirmListener" />-->
    <rabbit:listener queues="deadQueue" ref="deadQueueListener"/>
</rabbit:listener-container>

发送消息:

http://localhost:8081/test/send?exchange=exchangeTest&key=queueTestKey&message=hello

结果:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值