RabbitMq死信队列处理订单问题

1 篇文章 0 订阅
1 篇文章 0 订阅


前言

最近做一个新项目,碰到了有关的支付问题需要用到RabbitMq的一些特性去解决,简单记录一下自己的开发过程。


一、问题原因和分析

1)问题出现:

最近开发一个公众号的过程中,公司为了节省成本,并没有使用微信支付,而是对接了第三方支付。先看下第三方支付的基本流程:

前端 后端 第三方支付 (1)调用接口创建订单 (2)调用统一下单接口请求支付参数 (3)返回微信(H5)所需支付参数 (4)支付参数 (5)H5内支付(省略微信微信内过程) (6)根据预留回调地址返回支付状态 前端 后端 第三方支付

重点在第五步,这里展示给用户的其实就是输入密码这一步:
在这里插入图片描述
从第三步开始,这时候订单信息已经是在第三方了,并且第三方系统里订单状态是处理中,用户在输入密码的时候可以选择支付,也可以选择不支付直接退出。那么就出现了,两种情况:
1)用户直接支付(扣款成功与否)
2)用户不支付,直接退出。
情况分析:
①用户支付成功,第三方也有了收款信息,回调地址返回支付成功标志,这是最正常的情况,一切原有逻辑走。
②用户支付成功,第三方扣款信息有延迟,调用我回调地址的支付标志并非最终状态,这就需要我自己主动去查询。
③用户直接退出不支付,这时候,第三方直接返回给我订单处理中,这时候其实我怎么查,订单都是处理中,一直到订单有效期结束以后,第三方才会判定支付失败。
后两种情况,不管成功或者失败,第三方都不会主动告知,因为回调地址只调一次。有人也许会说,那你把第三步的支付参数保存下来,订单有效期内重新唤起支付页面不就行了。很遗憾,第三方只回了我“重新调”。

没办法,想办法解决吧

2)解决方案:
第一种:定时任务固定去查,用户发起支付,并且调起支付页面的时候我就开始轮询去查,只要订单状态是未支付,我就开始每隔5,15,35,65,125(s)去查,第二天每天晚上十二点一过,重新查一遍前一天未支付订单
第二种:RabbitMq延时队列+死信队列
这里重要介绍一下第二种。

二、使用步骤

1.导包

代码如下(示例):

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

2.配置类

这里贴出逻辑代码:

TTLMQConfig类

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * @author zhangqingfeng
 * @version 1.0
 * @email zhangqingfeng95@icloud.com
 * @date 2021/4/27 15:30
 */
@Configuration
public class TtlMQConfig {
	//因为有冗余代码,这里
    /**
     * 支付结果队列延迟5秒
     */
    public final static String TTL_PAY_QUEUE_5s = "pay.ttl.queue.5s";

    /**
     * 交换机名称
     */
    public final static String TTL_PAY_EXCHANGE = "pay.ttl.exchange";

   
    @Bean
    public DirectExchange ttlDirect(){
        return new DirectExchange(TTL_PAY_EXCHANGE,true,false);
    }

    /**
     * 5秒
     * @return
     */
    @Bean
    public Queue ttlQueue5s(){
        Map<String,Object> args = new HashMap<>();
        args.put("x-message-ttl",5000);
        args.put("x-dead-letter-exchange","pay.dead.exchange");
        args.put("x-dead-letter-routing-key","pay.dead.queue.5s");
        return new Queue(TTL_PAY_QUEUE_5s,true,false,false,args);
    }

    @Bean
    public Binding ttlDeadBinding5s(){
        return BindingBuilder.bind(ttlQueue5s()).to(ttlDirect()).with(TTL_PAY_QUEUE_5s);
    }

CommonMQConfig类:

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author zhangqingfeng
 * @version 1.0
 * @email zhangqingfeng95@icloud.com
 * @date 2021/4/27 15:44
 */
@Configuration
public class CommonMQConfig {

    /**
     * 支付结果队列名称
     */
    public final static String PAY_RESULT_QUEUE = "pay.result.queue";


    /**
     * 交换机名称
     */
    public final static String PAY_RESULT_EXCHANGE = "pay.result.exchange";

    /**
     * 声明队列
     * @return
     */
    @Bean
    public Queue resultQueue(){
        return new Queue(PAY_RESULT_QUEUE);
    }

    /**
     * 声明支付结果交换机
     * @return
     */
    @Bean
    public TopicExchange resultExchange(){
        return new TopicExchange(PAY_RESULT_EXCHANGE);
    }

    @Bean
    public Binding resultBinding(){
        return BindingBuilder.bind(resultQueue()).to(resultExchange()).with("pay.result.queue");
    }

}

DeadMQConfig类

package com.kangjiu.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author zhangqingfeng
 * @version 1.0
 * @email zhangqingfeng95@icloud.com
 * @date 2021/4/27 14:57
 */
@Configuration
public class DeadMQConfig {

    /**
     * 交换机名称
     */
    public final static String PAY_DEAD_EXCHANGE = "pay.dead.exchange";

    /**
     * 死信队列5s
     */
    public final static String PAY_DEAD_QUEUE_5s = "pay.dead.queue.5s";

    @Bean
    public DirectExchange deadDirect(){
        return new DirectExchange(PAY_DEAD_EXCHANGE,true,false);
    }

    /**
     * 5秒
     * @return
     */
    @Bean
    public Queue deadQueue5s(){
        return new Queue(PAY_DEAD_QUEUE_5s,true,false,false);
    }

    @Bean
    public Binding deadBinding5s(){
        return BindingBuilder.bind(deadQueue5s()).to(deadDirect()).with("pay.dead.queue.5s");
    }

PayController类

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;

/**
 * @author: 张青峰
 * @email: zhangqingfeng95@icloud.com
 * @date: 2021/4/24 12:39 下午
 * @description:
 */
@CrossOrigin
@RestController
@RequestMapping("/pay")
@Api(value = "支付接口", tags = {"支付接口"})
public class PayController {

    @Autowired
    private PayService payService;

    @PostMapping("/unifiedOrder")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "订单号",type = "String",name = "orderNo",paramType = "query",required = true),
            @ApiImplicitParam(value = "金额",type = "BigDecimal",name = "amount",paramType = "query",required = true)
    })
    public Result unifiedOrder(
            @RequestParam(value = "orderNo",required = false) String orderNo,
            @RequestParam(value = "amount",required = false) BigDecimal amount){
        return payService.unifiedOrder(orderNo,amount);
    }

    @PostMapping("/receivePayResult")
    @ApiOperation(value = "支付回调")
    public void receivePayResult(HttpServletRequest request, HttpServletResponse response){
        payService.receivePayResult(request,response);
    }

}

测试类

 @Test
    public void testSendTTL(){
        Map<String,Object> resultMap = new HashMap<>();
        resultMap.put("data","data");
        resultMap.put("sign","sign");
        rabbitTemplate.convertAndSend(TtlMQConfig.TTL_PAY_EXCHANGE,TtlMQConfig.TTL_PAY_QUEUE_5s, JSON.toJSONString(resultMap));
    }

三、总结

以上内容仅仅是对自己学习和开发过程中的一些记录,并不是完整代码,如果有想一起讨论的可以评论留言。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值