rabbitmq 延时队列,实现类似微信支付宝 阶梯性异步回调通知

一般支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。

对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)
废话不多说 (先理下思路)  下面以生产消费者 模式  进行梳理

1.TTL:存活时间
2.死信:过了存活期的消息
3.Dead Letter Exchange:"死信" 交换器  其实就是普通的交换器而已.Exchange有4个类型:direct,topic,fanout,header
4.routingkey:消费者队列绑定到交换机时要指定路由key (详细请看:
rabbitmq-----Routing和topic模式)

1.首先  在Connection中创建一个Channel,通过Channel声明两个交换器,一个是 用来接收通知数据的交换器ExchangeA,一个是延时通知数据的交换器ExchangeB。

2.声明两个队列,一个"死信"队列queueA,一个延时通知数据队列queueB。

3.然后,交换器ExchangeA和 "死信"队列queueA  绑定,把延时通知数据的交换器ExchangeB和queueB进行绑定。交换器再和队列进行绑定时会设置routingkey

<?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.xsd
          http://www.springframework.org/schema/rabbit
          http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">


    <!--配置connection-factory,指定连接rabbit server参数 -->
    <rabbit:connection-factory id="connectionFactory" virtual-host="/" username="${USERNAME}" password="${PASSWORD}" host="${HOST}" port="${PORT}"/>

    <!--定义rabbit模版,指定连接工厂以及定义exchange-->
    <rabbit:template id="amqpTemplate" connection-factory="connectionFactory" />

    <!--MQ的管理,包括队列,交换器,声明等-->
    <rabbit:admin connection-factory="connectionFactory" />


<!--定义一个交换机exchangeA,路由类型为direct,所有的订单会塞给此交换机 和 然后和queueA 绑定 key testQueue-->
 <rabbit:direct-exchange name="exchangeA" delayed="false"  id="exchangeA" durable="false" auto-delete="false">
     <rabbit:bindings>
         <rabbit:binding queue="queueA" key="testQueue"/>
     </rabbit:bindings>
</rabbit:direct-exchange>
<!--定义一个延时交换机,路由类型为direct,延时的数据会塞给此交换机 并和exchangeB绑定-->
<rabbit:direct-exchange name="exchangeB"  id="exchangeB" delayed="false" durable="false" auto-delete="false">
        <rabbit:bindings>
            <rabbit:binding queue="queueB" key="testQueue"/>
        </rabbit:bindings>
</rabbit:direct-exchange>

<!--定义队列,queueA-->
<rabbit:queue name="queueA" durable="false"/>

<!--定义延时队列,queueB-->
<rabbit:queue name="queueB" durable="false">
      <rabbit:queue-arguments>
            <!--  队列绑定参数 告诉rabbit 将超时的数据推送到 死信交换机-->
            <entry key="x-dead-letter-exchange" value="exchangeA" />
            <entry key="x-dead-letter-routing-key" value="testQueue" />
      </rabbit:queue-arguments>
</rabbit:queue>
        
    

<!--队列监听-->
<rabbit:listener-container connection-factory="connectionFactory">
    <rabbit:listener ref="myCustomer" method="listen" queue-names="queueA" />
    <rabbit:listener ref="myCustomer" method="listen" queue-names="queueA" />
    <rabbit:listener ref="myCustomer" method="listen" queue-names="queueA" />
</rabbit:listener-container>

<!--定义消费者-->
<bean id="myCustomer" class="xxx.RabbitCustomer" />

4.创建生产者类(下面贴出部分代码 具体按自己需求实现) RabbitService

public static void publish(String queue, String message, long delay) {
        try {
            Channel channel = connection.createChannel();//创建一个channel,不管是生产数据,还是消费数据,都是通过channel去操作的
            AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
                builder.contentType("text/plain");
            if(delay ==0){
                AMQP.BasicProperties properties = builder.build();
                //如果延时为0 走死信交换器
                channel.basicPublish("exchangeA",
                                    "testQueue",
                                    properties,
                                    message.getBytes()
                );
            }else{
                //否则推送到延时交换器
                builder.deliveryMode(2);//设置消息持久化
                builder.expiration(String.valueOf(delay*1000)); //设置延时时间  rabbitmq  是毫秒
                AMQP.BasicProperties properties = builder.build();
                channel.basicPublish("exchangeB","testQueue", properties, message.getBytes());
            }

            channel.close();
        } catch (Exception e) {
            logger.error("rabbit-publish-error:{}", e.getMessage());
        }
    }

5.创建消费者类  RabbitCustomer(下面部分代码)

//message 为json数据
public void listen(String message) {
        try {
            JSONObject jsonData;
            Integer pushNum;
            try {
                jsonData= loadMessage(message);
                pushNum= paras.getInteger("pushNum");
            } catch (Exception e) {
                logger.error("异步通知错误:", e.getMessage());
                return;
            }
            String result = null;
            try {
                  result = doHttpNotify(jsonData, connectionsPoolExecutor);
            } catch (Exception e) {
                logger.error("异步通知错误:"+e.getMessage());
            }
            logger.info("异步通知返回:" + result);

            if (StringUtils.isNotBlank(result) && result.length() >= 60) {
                result = result.substring(0, 60);
            }
            
            if (StringUtils.isNotBlank(result) && "success".equals(result.trim())) {
                    //返回成功的逻辑
            } else {
                //处理失败的逻辑 然后准备 重新发起通知
               paras.put("pushNum", pushNum+ 1);
               if (retry == 0) {
                    RabbitService.publish(jsonData.toJSONString(), 5);  //5s
                } else if (retry == 1) {
                    RabbitService.publish(jsonData.toJSONString(), 30);  //30s
                } else if (retry == 2) {
                    RabbitService.publish( jsonData.toJSONString(), 120); //120s
                } else {
                    logger.error("异步通知失败");
                }
            }
        } catch (Exception e) {
            logger.error("异步通知失败" + e.getMessage());
        } finally {
            
        }

上方会有个问题,但要想实现这个功能,需要创建几个不同时间的延时队列,根据延时参数写入到不同的队列,过期了交给死信交换机即可

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值