Spring Boot 訂單超時自動取消的 3 種主流實現方案

Spring Boot 訂單超時自動取消的 3 種主流實現方案

關鍵詞:Spring Boot、訂單超時、延遲任務、RabbitMQ、Redis、定時任務

在電商、外賣、票務等業務中,“下單后若 30 分鐘未支付則自動取消”是一道經典需求。實現方式既要保證 實時性,又要在 高并發 下保持 低成本、高可靠
本文基于 Spring Boot,給出 3 種生產級落地方案,并附完整代碼與選型對比,方便快速決策。


一、需求拆解

功能點約束
觸發條件創建時間 + 30 min 仍未支付
實時性秒級(理想) / 分鐘級(可接受)
冪等重復取消需冪等
高并發峰值 10 w+/日
數據一致性不能漏單、不能錯單

二、方案總覽

方案核心機制實時性額外組件代碼復雜度
① 定時任務@Scheduled + DB 掃描分鐘級★☆☆
② 延遲隊列RabbitMQ TTL + DLX秒級RabbitMQ★★☆
③ Redis 過期事件Key TTL + Keyspace Notify秒級Redis★★☆

三、方案 1:定時任務(@Scheduled)

1. 思路

周期性掃描訂單表,把“創建時間 + 30 min < 當前時間”且狀態為 PENDING 的訂單置為 CANCELLED

2. 代碼實現

@EnableScheduling
@Component
@RequiredArgsConstructor
public class OrderCancelSchedule {private final OrderService orderService;/** 每 30s 跑一次,可根據數據量調整 */@Scheduled(fixedDelay = 30_000)public void cancelUnpaidOrders() {LocalDateTime expirePoint = LocalDateTime.now().minusMinutes(30);List<Long> ids = orderService.findUnpaidBefore(expirePoint);if (!ids.isEmpty()) {int affected = orderService.batchCancel(ids);log.info("自動取消訂單 {} 條", affected);}}
}

3. 優化技巧

  • 分頁 + 索引
    CREATE INDEX idx_order_status_created ON t_order(status, created_time);
    
  • 分片掃描:按 ID 或時間分片,避免大表鎖。
  • 單機多線程@Async("cancelExecutor") + 線程池。

4. 優缺點

  • ? 零依賴、實現快
  • ? 數據量大時 DB 壓力大;實時性受輪詢間隔限制

5. 適用場景

日訂單 < 1 w,或作為兜底方案。


四、方案 2:RabbitMQ 延遲隊列

1. 思路

訂單創建后發送一條 30 min TTL 的消息;到期自動路由到消費隊列,消費者檢查訂單狀態并取消。

2. 架構圖

Producer ──> Delay Exchange (x-delayed-message) ──> 30min TTL ──> Cancel Queue ──> Consumer

3. 代碼實現

3.1 聲明交換機 & 隊列
@Configuration
public class RabbitDelayConfig {@Beanpublic CustomExchange delayExchange() {Map<String, Object> args = Map.of("x-delayed-type", "direct");return new CustomExchange("order.delay", "x-delayed-message", true, false, args);}@Beanpublic Queue cancelQueue() {return QueueBuilder.durable("order.cancel.queue").build();}@Beanpublic Binding binding() {return BindingBuilder.bind(cancelQueue()).to(delayExchange()).with("order.cancel").noargs();}
}
3.2 發送延遲消息
@Service
@RequiredArgsConstructor
public class OrderPublisher {private final RabbitTemplate rabbitTemplate;public void createOrder(Order order) {// 1. 落庫orderMapper.insert(order);// 2. 發送延遲消息rabbitTemplate.convertAndSend("order.delay","order.cancel",order.getId(),msg -> {msg.getMessageProperties().setDelay(30 * 60 * 1000); // 30 minreturn msg;});}
}
3.3 消費并取消
@Component
@RabbitListener(queues = "order.cancel.queue")
public class CancelConsumer {private final OrderService orderService;@RabbitHandlerpublic void handle(Long orderId) {Order order = orderService.find(orderId);if (order != null && order.getStatus() == OrderStatus.PENDING) {orderService.cancel(orderId);}}
}

4. 優缺點

  • ? 實時性好(秒級);支持分布式;消息持久化
  • ? 需要 RabbitMQ;鏈路更長

5. 適用場景

中高并發,需秒級取消,已用 MQ 或愿意引入 MQ。


五、方案 3:Redis Keyspace 過期事件

1. 思路

order:{id} 作為 key,30 min TTL;Redis 鍵過期時推送事件;應用監聽后取消訂單。

2. Redis 配置

# redis.conf
notify-keyspace-events Ex

或 CLI:

CONFIG SET notify-keyspace-events Ex

3. 代碼實現

3.1 訂單創建時寫 Redis
@Service
public class OrderService {private final StringRedisTemplate redisTemplate;public void createOrder(Order order) {orderMapper.insert(order);// value 隨意,這里用 idredisTemplate.opsForValue().set("order:" + order.getId(),String.valueOf(order.getId()),Duration.ofMinutes(30));}
}
3.2 監聽過期事件
@Configuration
public class RedisListenerConfig {@Beanpublic RedisMessageListenerContainer container(RedisConnectionFactory cf) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(cf);container.addMessageListener((message, pattern) -> {String key = message.toString();if (key.startsWith("order:")) {String orderId = key.substring(6);// 冪等取消orderService.cancelIfUnpaid(Long.valueOf(orderId));}},new PatternTopic("__keyevent@*__:expired"));return container;}
}

4. 冪等 & 可靠性

  • 冪等:取消 SQL 加狀態條件 WHERE status = PENDING
  • 可靠性:Redis 重啟會丟失未過期 key,需 兜底定時任務(方案 1)雙保險。

5. 優缺點

  • ? 實時性高,組件少
  • ? Redis 重啟可能丟事件;需處理冪等

6. 適用場景

已用 Redis,訂單量中等,能接受極低概率漏單。


六、3 種方案對比與選型

維度定時任務RabbitMQ 延遲隊列Redis 過期事件
實時性分鐘級秒級秒級
吞吐量
額外組件RabbitMQRedis
可靠性中(需兜底)
實現復雜度★☆☆★★☆★★☆
推薦場景小流量、兜底高并發、已用 MQ已用 Redis、中等并發

建議

  1. 小項目 → 定時任務即可;
  2. 大流量 → 延遲隊列;
  3. 已用 Redis → 過期事件 + 定時任務兜底雙保險。

七、灰度 & 監控

  • 灰度發布:按用戶尾號或城市分批切換方案。
  • 監控指標
    • 取消成功率
    • MQ 消息積壓
    • Redis 過期 QPS
    • 定時任務掃描耗時

八、小結

一句話總結
定時任務 簡單但慢;延遲隊列 實時但重;Redis 過期 輕量但需兜底。

在實際落地中,可以 并行運行 兩種方案(如延遲隊列 + 兜底定時任務),通過配置開關靈活切換,確保業務永遠在線。祝你的訂單永不超賣!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/89718.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/89718.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/89718.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

0401聚類-機器學習-人工智能

文章目錄一 無監督學習什么是無監督學習&#xff1f;核心特點&#xff1a;無監督學習的主要類型1. 聚類分析 (Clustering)2. 降維 (Dimensionality Reduction)3. 關聯規則學習 (Association Rule Learning)4. 異常檢測 (Anomaly Detection)5. 密度估計 (Density Estimation)二 …

基礎神經網絡模型搭建

nn 包提供通用深度學習網絡的模塊集合&#xff0c;接收輸入張量&#xff0c;計算輸出張量&#xff0c;并保存權重。通常使用兩種途徑搭建 PyTorch 中的模型&#xff1a;nn.Sequential和 nn.Module。 nn.Sequential通過線性層有序組合搭建模型&#xff1b;nn.Module通過__init__…

基于單片機出租車計價器設計

傳送門 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目速選一覽表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目功能速覽 概述 本設計實現了一種基于單片機的智能化出租車計價系統。系統以單片機為核心處理器&#xff0c;集成…

134. Java 泛型 - 上限通配符

文章目錄134. Java 泛型 - 上限通配符 (? extends T)**1. 什么是上限通配符 (? extends T)&#xff1f;****2. 為什么使用 ? extends T&#xff1f;****3. 示例&#xff1a;使用 ? extends T 進行數據讀取****? 示例 1&#xff1a;計算數值列表的總和****4. 注意事項&…

【1】YOLOv13 AI大模型-可視化圖形用戶(GUI)界面系統開發

【文章內容適用于任意目標檢測任務】【GUI界面系統不局限于YOLOV13&#xff0c;主流YOLO系列模型同樣適用】本文以車輛行人檢測為背景&#xff0c;介紹基于【YOLOV13模型】和【AI大模型】的圖形用戶&#xff08;GUI&#xff09;界面系統的開發。助力大論文實現目標檢測模型的應…

小程序常用api

1. wx.request - 發起網絡請求 用于向服務器發送 HTTP 請求&#xff0c;獲取數據或提交表單。 // 示例&#xff1a;GET 請求獲取數據 wx.request({url: https://api.example.com/data, // 替換為實際 API 地址method: GET,success: (res) > {console.log(請求成功, res.da…

PaliGemma 2-輕量級開放式視覺語言模型

PaliGemma 2是輕量級開放式視覺語言模型 (VLM)&#xff0c;靈感源自 PaLI-3&#xff0c;基于 SigLIP 視覺模型和 Gemma 語言模型等開放式組件。PaliGemma 同時接受圖片和文本作為輸入&#xff0c;并且可以回答有關圖片的詳細問題和背景信息。PaliGemma 2 提供 30 億、100 億和 …

騰訊云云服務器深度介紹

以下是圍繞騰訊云云服務器&#xff08;CVM&#xff09;的詳細介紹與推薦文章&#xff0c;結合其核心優勢、應用場景及技術特性&#xff0c;為不同用戶群體提供參考&#xff1a; &#x1f680; 一、產品定位與核心價值 騰訊云云服務器&#xff08;Cloud Virtual Machine, CVM&a…

Ceph OSD.419 故障分析

Ceph OSD.419 故障分析 1. 問題描述 在 Ceph 存儲集群中&#xff0c;OSD.419 無法正常啟動&#xff0c;系統日志顯示服務反復重啟失敗。 2. 初始狀態分析 觀察到 OSD.419 服務啟動失敗的系統狀態&#xff1a; systemctl status ceph-osd419 ● ceph-osd419.service - Ceph obje…

MySQL持久化原理及其常見問題

目錄 MySQL刷盤原理 臟頁和干凈頁 MySQL出現短暫的堵塞SQL現象 情況分析 應對措施 數據庫表中數據刪除原理 刪除表中數據數據庫空間大小不會改變 情況分析 應對措施 MySQL刷盤原理 一般主要分為兩個步驟 內存更新和 redo log 記錄是同一事務修改的兩個必要操作&#…

VSCode中Cline無法正確讀取終端的問題解決

出現的問題是&#xff1a;Cline 無法正確讀取終端輸出。 Shell Integration Unavailable Cline won’t be able to view the command’s output. Please update VSCode (CMD/CTRL Shift P → “Update”) and make sure you’re using a supported shell: zsh, bash, fish, o…

scalelsd 筆記 線段識別 本地部署 模型架構

ant-research/scalelsd | DeepWiki https://arxiv.org/html/2506.09369?_immersive_translate_auto_translate1 https://gitee.com/njsgcs/scalelsd https://github.com/ant-research/scalelsd https://huggingface.co/cherubicxn/scalelsd 模型鏈接&#xff1a; https…

Python, C ++開發個體戶/個人品牌打造APP

個體戶/個人品牌打造APP開發方案&#xff08;Python C&#xff09;一、技術選型與分工1. Python- 核心場景&#xff1a;后端API開發、數據處理、內容管理、第三方服務集成&#xff08;如社交媒體分享、支付接口&#xff09;。- 優勢&#xff1a;開發效率高&#xff0c;豐富的庫…

SQLAlchemy 常見問題筆記

文章目錄SQLAlchemy Session對象如何操作數據庫SQLAlchemy非序列化對象如何返回1.問題分析2.解決方案方法1&#xff1a;使用 Pydantic 響應模型&#xff08;推薦&#xff09;方法2&#xff1a;手動轉換為字典&#xff08;簡單快速&#xff09;方法3&#xff1a;使用 SQLAlchemy…

Shell腳本-uniq工具

一、前言在 Linux/Unix 系統中&#xff0c;uniq 是一個非常實用的文本處理命令&#xff0c;用于對重復的行進行統計、去重和篩選。它通常與 sort 搭配使用&#xff0c;以實現高效的文本數據清洗與統計分析。無論是做日志分析、訪問頻率統計&#xff0c;還是編寫自動化腳本&…

氛圍編碼(Vice Coding)的工具選擇方式

一、前言 在寫作過程中&#xff0c;我受益于若干優秀的博客分享&#xff0c;它們給予我寶貴的啟發&#xff1a; 《5分鐘選對AI編輯器&#xff0c;每天節省2小時開發時間讓你早下班&#xff01;》&#xff1a;https://mp.weixin.qq.com/s/f0Zm3uPTcNz30oxKwf1OQQ 二、AI編輯的…

[硬件電路-57]:根據電子元器件的受控程度,可以把電子元器件分為:不受控、半受控、完全受控三種大類

根據電子元器件的受控程度&#xff0c;可將其分為不受控、半受控、完全受控三大類。這種分類基于元器件的工作狀態是否需要外部信號&#xff08;如電壓、電流、光、熱等&#xff09;的主動調控&#xff0c;以及調控的精確性和靈活性。以下是具體分類及實例說明&#xff1a;一、…

基于Pytorch的人臉識別程序

人臉識別原理詳解人臉識別是模式識別和計算機視覺領域的重要研究方向&#xff0c;其目標是從圖像或視頻中識別出特定個體的身份。現代人臉識別技術主要基于深度學習方法&#xff0c;特別是卷積神經網絡 (CNN)&#xff0c;下面從多個維度詳細解析其原理&#xff1a;1. 人臉識別的…

ubuntu 開啟ssh踩坑之旅

文章目錄確認當前用戶為普通用戶 or root命令使用ssh還是sshd服務名稱的由來apt update和apt upgrade的關系apt upgrade報錯&#xff1a;“E: 您在 /var/cache/apt/archives/ 上沒有足夠的可用空間”開啟ssh步驟錯誤排查查看日志修改sshd_config文件允許防火墻通過22端口確認當…

力扣:動態規劃java

sub07 線性DP - O(1) 狀態轉移2_嗶哩嗶哩_bilibili 跳樓梯 class Solution {public int climbStairs(int n) {if (n < 1) {return 1; // 處理邊界情況}int[] dp new int[n 1]; // 創建長度為n1的數組&#xff0c;比方說跳二級樓梯dp[0] 1; // 初始值設定dp[1] 1;for (…