rabbitMq延迟队列实现订单失败(订单过期)
1.订单失效原理
订单失效的实现方式
1:redis的过期特性,redis提供了key过期的监听事件接口,通过监听key过期来实现订单失效,不支持集群环境(主从结构存在数据副本)
2:使用rabbitMq实现延迟队列的功能。
- 当生成订单时,将订单号放入死信队列(因为没有消息处理者,所以称为死信队列)设置消息的存活时间为30分钟,
- 当30分钟过后,死信队列的消息会通过,路由转发交换机,路由转发交换机将消息发给工作队列
- 消息处理者处理消息
2.具体实现
- 导入相应的jar包
<!--spring整合rabbitMq -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.1.2</version>
</dependency>
<!-- spring整合rabbitMq-->
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
- 配置rabbit配置文件spring-rabbit.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: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">
<!-- 连接工厂-->
<!-- 创建连接工厂将来生产连接,工厂名为rabbitConnectionFactory,登陆名为admin,密码为admin,连接地址为192.168.192.3 ,端口号为5672-->
<rabbit:connection-factory id="rabbitConnectionFactory" username="admin" password="admin" host="192.168.192.3" port="5672"/>
<!--以管理员身份使用上面这个连接工厂 -->
<rabbit:admin connection-factory="rabbitConnectionFactory"/>
<!-- 死信队列,队列名为dead_queue。自动声明-->
<rabbit:queue name="dead_queue" auto-declare="true">
<!-- 定义队列参数: -->
<rabbit:queue-arguments>
<!-- 设置消息的存活时间 毫秒数-->
<entry key="x-message-ttl" value="30000" value-type="java.lang.Long"/>
<!-- 引用死信路由器 -->
<entry key="x-dead-letter-exchange" value="dead_exchange"/>
<!-- 设置死信路由键 -->
<entry key="x-dead-letter-routing-key" value="task_queue"/>
</rabbit:queue-arguments>
</rabbit:queue>
<!-- dead_exchange死信交换机-->
<rabbit:direct-exchange name="dead_exchange" durable="false" auto-delete="false" id="dead_exchange">
<!-- 死信理由绑定转发的队列-->
<rabbit:bindings>
<rabbit:binding queue="task_queue"/>
</rabbit:bindings>
</rabbit:direct-exchange>
<!-- 任务队列-->
<!-- 消费者所获取的消息队列-->
<rabbit:queue name="task_queue" auto-declare="true" />
<!-- 线程池-->
<!-- 消费者消费消息的任务类
线程池去调度线程去执行任务类的方法
-->
<!-- 配置线程池 -->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 线程池维护线程的最少数量 -->
<property name="corePoolSize" value="5"/>
<!-- 线程池维护线程所允许的空闲时间 -->
<property name="keepAliveSeconds" value="30000"/>
<!-- 线程池维护线程的最大数量 -->
<property name="maxPoolSize" value="2000"/>
<!-- 线程池所使用的缓冲队列 -->
<property name="queueCapacity" value="20"/>
</bean>
<!-- 监听程序监听任务队列消费-->
<!-- 配置监听容器-->
<rabbit:listener-container connection-factory="rabbitConnectionFactory" acknowledge="auto" task-executor="taskExecutor">
<!-- 指定监听队列 使用哪个监听类, 定义了 收到消息之后的逻辑 ,来监听-->
<rabbit:listener queues="task_queue" ref="delayTask"/>
</rabbit:listener-container>
<!-- 消息消费的任务类-->
<bean id="delayTask" class="com.oracle.shop.task.OrdersExpireMessageTask"/>
<!-- 配置生产消息的客户端对象,将来发送消息到dead_queue -->
<!-- 实例化一个生产者客户端对象,将来向dead_queue写消息
spring封装了amqpTempalte
-->
<rabbit:template id="amqpTemplate" connection-factory="rabbitConnectionFactory" queue="dead_queue" routing-key="dead_queue"/>
</beans>
- 编写处理类
//订单过期处理
public class OrdersExpireMessageTask implements MessageListener {
@Autowired
private OrdersService ordersService;
//消费者监听队列进行处理
@Override
public void onMessage(Message message) {
//消息过期后,接收并进行处理
String orderNumber = new String(message.getBody());
//查询订单状态,如果未支付,则查询支付宝接口,确认订单状态,然后进行操作
Orders orders = ordersService.selectOrdersByOrdersNum(orderNumber);
if (orders.getOrdersStatus() == 0) {//未支付
//通过支付包验证订单是否支付
try {
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID,
AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET,
AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE);
//创建查询请求
AlipayTradeQueryRequest alipayTradeQueryRequest = new AlipayTradeQueryRequest();
//设置请求参数
alipayTradeQueryRequest.setBizContent("{" + "\"out_trade_no\":\"" + orderNumber + "\"" + "}");
//执行查询,返回查询结果
AlipayTradeQueryResponse alipayTradeQueryResponse = alipayClient.execute(alipayTradeQueryRequest);
//对查询结果进行处理
if (alipayTradeQueryResponse.isSuccess()) {
switch (alipayTradeQueryResponse.getTradeStatus()) // 判断交易结果
{
case "TRADE_FINISHED": // 交易结束并不可退款,修改订单状态为已支付
ordersService.updateOrdersStatusPaied(orderNumber);
break;
case "TRADE_SUCCESS": // 交易支付成功,修改订单状态为,已支付
ordersService.updateOrdersStatusPaied(orderNumber);
break;
case "TRADE_CLOSED": // 未付款交易超时关闭或支付完成后全额退款,修改订单状态为,未支付
ordersService.updateOrdersStatusOverdue(orderNumber);
break;
case "WAIT_BUYER_PAY": // 交易创建并等待买家付款,修改订单状态为,未支付
ordersService.updateOrdersStatusOverdue(orderNumber);
break;
default:
break;
}
} else {//支付宝方面没有该订单,直接修改订单信息为过期
ordersService.updateOrdersStatusOverdue(orderNumber);
}
} catch (AlipayApiException e) {
e.printStackTrace();
}
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了