【RabbitMQ】11 深入部分P4 延迟队列

一、延迟队列:

消息经过交换机分配到队列上之后,在到达指定的时间,才会被消费?

 

 

需求:

1、下单之后的30分钟,用户未支付,订单取消,回滚库存

2、新用户注册7天后,发送短信慰问,或者是用户生日发送短信祝福

 

业务事件触发之后进入一个时间间隔,消息在这个间隔量的节点上再执行

 

 

 

Java程序提供了一些定时任务的框架,可以调用Java程序实现

或者是消息中间件自己做的延迟队列

是使用程序解决还是用消息队列解决,取决于现实业务的考量

 

 

二、RabbitMQ的技术方案:

RabbitMQ没有延迟队列这样的功能提供

可以使用 TTL + DLX 组合实现延迟队列的效果

 

 三、RabbitMQ代码实现:

 

<?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:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
    <!--加载配置文件-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <!-- 定义rabbitmq connectionFactory

        publisher-confirms="true" 消息发送可确认
        publisher-returns="true"
    -->
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"
                               publisher-confirms="true"
                               publisher-returns="true"
    />
    <!--定义管理交换机、队列-->
    <rabbit:admin connection-factory="connectionFactory"/>

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


    <!--
        延迟队列
            1、定义正常的交换机和队列
            2、定义死信的交换机和队列
            3、绑定,并设置正常过期时间为30分钟

        delay
    -->

    <!-- 正常 -->
    <rabbit:queue id="delay-regular-Q" name="delay-regular-Q">
        <rabbit:queue-arguments>
            <!-- 绑定分配死信-->
            <entry key="x-dead-letter-exchange" value="delay-dead-X" />
            <entry key="x-dead-letter-routing-key" value="dlx.order.cancel"/>

            <!-- TTL超时限定 -->
            <entry key="x-message-ttl" value="10000" value-type="java.lang.Integer" />
        </rabbit:queue-arguments>
    </rabbit:queue>

    <rabbit:topic-exchange name="delay-regular-X" >
        <rabbit:bindings>
            <rabbit:binding pattern="order.#" queue="delay-regular-Q"/>
        </rabbit:bindings>
    </rabbit:topic-exchange>

    <!-- 死信 -->
    <rabbit:queue id="delay-dead-Q" name="delay-dead-Q" />
    <rabbit:topic-exchange name="delay-dead-X" >
        <rabbit:bindings>
            <rabbit:binding pattern="dlx.order.#" queue="delay-dead-Q"/>
        </rabbit:bindings>
    </rabbit:topic-exchange>

</beans>

测试类编写发送一条消息:

    @Test
    public void delayTest() {
        //
        rabbitTemplate.convertAndSend(
                "delay-regular-X",
                "order.info",
                "延迟队列测试 。。。。 在死信队列查看此消息 ");
    }

消费者服务编写延迟队列的监听器

注意这个监听器是监听死信队列的

package cn.dzz.rabbitmq.listener;

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

import java.nio.charset.StandardCharsets;
@Component
public class DelayListener implements ChannelAwareMessageListener {
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        long deliveryTag = 0;
        try {

            System.out.println(new String(message.getBody(), StandardCharsets.UTF_8));

            // 处理业务逻辑
            // todo...

            /**
             *
             * 根据接收的ID查询订单状态
             * 判断状态是否为支付成功
             * 判断状态是否为支付成功
             * 取消订单, 回滚库存
             * 
             */

            int i = 10 / 0; // 这个异常会被捕获,然后触发RabbitMQ一直让消息重新入列发送

            // 业务签收
            deliveryTag = message.getMessageProperties().getDeliveryTag();

            channel.basicAck(deliveryTag, true);

        } catch (Exception exception) {
            // exception.printStackTrace();
            System.out.println("已经触发异常Catch 消息被拒绝 .... ");
            channel.basicNack(deliveryTag, true, false); // 被拒绝的消息不再重回队列, 这样这个消息才会分配到死信队列中
            
        }
    }
}

监听器XML配置:

<?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:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
    <!--加载配置文件-->
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    <!-- 定义rabbitmq connectionFactory -->
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>

    <context:component-scan base-package="cn.dzz.rabbitmq.listener" />

    <!--
        定义监听器容器
        acknowledge="manual" 默认就是none
    -->
    <rabbit:listener-container connection-factory="connectionFactory" auto-declare="true" acknowledge="manual">
        <rabbit:listener ref="delayListener" queue-names="delay-dead-Q" />
    </rabbit:listener-container>

</beans>

消费者服务经过延迟后收到消息:

监听测试
延迟队列测试 。。。。 在死信队列查看此消息 
已经触发异常Catch 消息被拒绝 .... 

 

 

 

 

 

 

 

 

posted @ 2021-10-10 10:26  emdzz  阅读(80)  评论(0编辑  收藏  举报