自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

訂單超時自動取消的七種方案,我用這種!

開發(fā) 前端
每種方案都有自己的適用場景,大家在選擇的時候,記得結(jié)合業(yè)務(wù)需求、訂單量、并發(fā)量來綜合考慮。如果你的項目規(guī)模較小,可以直接用延時隊列或 Redis;而在大型高并發(fā)系統(tǒng)中,消息隊列和事件流處理往往是首選。

前言

在電商、外賣、票務(wù)等系統(tǒng)中,訂單超時未支付自動取消是一個常見的需求。

這個功能乍一看很簡單,甚至很多初學(xué)者會覺得:"不就是加個定時器么?" 但真到了實際工作中,細(xì)節(jié)的復(fù)雜程度往往會超乎預(yù)期。

這里我們從基礎(chǔ)到高級,逐步分析各種實現(xiàn)方案,最后分享一些在生產(chǎn)中常見的優(yōu)化技巧,希望對你會有所幫助。

1. 使用延時隊列(DelayQueue)

適用場景:訂單數(shù)量較少,系統(tǒng)并發(fā)量不高。

延時隊列是Java并發(fā)包(java.util.concurrent)中的一個數(shù)據(jù)結(jié)構(gòu),專門用于處理延時任務(wù)。

訂單在創(chuàng)建時,將其放入延時隊列,并設(shè)置超時時間。

延時時間到了以后,隊列會觸發(fā)消費邏輯,執(zhí)行取消操作。

示例代碼:

import java.util.concurrent.*;

public class OrderCancelService {
    private static final DelayQueue<OrderTask> delayQueue = new DelayQueue<>();

    public static void main(String[] args) throws InterruptedException {
        // 啟動消費者線程
        new Thread(() -> {
            while (true) {
                try {
                    OrderTask task = delayQueue.take(); // 獲取到期任務(wù)
                    System.out.println("取消訂單:" + task.getOrderId());
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }).start();

        // 模擬訂單創(chuàng)建
        for (int i = 1; i <= 5; i++) {
            delayQueue.put(new OrderTask(i, System.currentTimeMillis() + 5000)); // 5秒后取消
            System.out.println("訂單" + i + "已創(chuàng)建");
        }
    }

    static class OrderTask implements Delayed {
        private final long expireTime;
        private final int orderId;

        public OrderTask(int orderId, long expireTime) {
            this.orderId = orderId;
            this.expireTime = expireTime;
        }

        public int getOrderId() {
            return orderId;
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert(expireTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        }

        @Override
        public int compareTo(Delayed o) {
            return Long.compare(this.expireTime, ((OrderTask) o).expireTime);
        }
    }
}

優(yōu)點:

  • 實現(xiàn)簡單,邏輯清晰。

缺點:

  • 依賴內(nèi)存,系統(tǒng)重啟會丟失任務(wù)。
  • 隨著訂單量增加,內(nèi)存占用會顯著上升。

2. 基于數(shù)據(jù)庫輪詢

適用場景:訂單數(shù)量較多,但系統(tǒng)對實時性要求不高。

輪詢是最容易想到的方案:定期掃描數(shù)據(jù)庫,將超時的訂單狀態(tài)更新為“已取消”。

示例代碼:

public void cancelExpiredOrders() {
    String sql = "UPDATE orders SET status = 'CANCELLED' WHERE status = 'PENDING' AND create_time < ?";
    try (Connection conn = dataSource.getConnection();
         PreparedStatement ps = conn.prepareStatement(sql)) {
        ps.setTimestamp(1, new Timestamp(System.currentTimeMillis() - 30 * 60 * 1000)); // 30分鐘未支付取消
        int affectedRows = ps.executeUpdate();
        System.out.println("取消訂單數(shù)量:" + affectedRows);
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

優(yōu)點:

  • 數(shù)據(jù)可靠性強,不依賴內(nèi)存。
  • 實現(xiàn)成本低,無需引入第三方組件。

缺點:

  • 頻繁掃描數(shù)據(jù)庫,會帶來較大的性能開銷。
  • 實時性較差(通常定時任務(wù)間隔為分鐘級別)。

優(yōu)化建議:

  • 為相關(guān)字段加索引,避免全表掃描。
  • 結(jié)合分表分庫策略,減少單表壓力。

3. 基于Redis隊列

適用場景:適合對實時性有要求的中小型項目。

Redis 的 List 或 Sorted Set 數(shù)據(jù)結(jié)構(gòu)非常適合用作延時任務(wù)隊列。

我們可以把訂單的超時時間作為 Score,訂單 ID 作為 Value 存到 Redis 的 ZSet 中,定時去取出到期的訂單進(jìn)行取消。

例子:

public void addOrderToQueue(String orderId, long expireTime) {
    jedis.zadd("order_delay_queue", expireTime, orderId);
}

public void processExpiredOrders() {
    long now = System.currentTimeMillis();
    Set<String> expiredOrders = jedis.zrangeByScore("order_delay_queue", 0, now);
    for (String orderId : expiredOrders) {
        System.out.println("取消訂單:" + orderId);
        jedis.zrem("order_delay_queue", orderId); // 刪除已處理的訂單
    }
}

優(yōu)點:

  • 實時性高。
  • Redis 的性能優(yōu)秀,延遲小。

缺點:

  • Redis 容量有限,適合中小規(guī)模任務(wù)。
  • 需要額外處理 Redis 宕機(jī)或數(shù)據(jù)丟失的問題。

4. Redis Key 過期回調(diào)

適用場景:對超時事件實時性要求高,并且希望依賴 Redis 本身的特性實現(xiàn)簡單的任務(wù)調(diào)度。

Redis 提供了 Key 的過期功能,結(jié)合 keyevent 事件通知機(jī)制,可以實現(xiàn)訂單的自動取消邏輯。

當(dāng)訂單設(shè)置超時時間后,Redis 會在 Key 過期時發(fā)送通知,我們只需要訂閱這個事件并進(jìn)行相應(yīng)的處理。

例子:

  • 設(shè)置訂單的過期時間:
public void setOrderWithExpiration(String orderId, long expireSeconds) {
    jedis.setex("order:" + orderId, expireSeconds, "PENDING");
}
  • 訂閱 Redis 的過期事件:
public void subscribeToExpirationEvents() {
    Jedis jedis = new Jedis("localhost");
    jedis.psubscribe(new JedisPubSub() {
        @Override
        public void onPMessage(String pattern, String channel, String message) {
            if (channel.equals("__keyevent@0__:expired")) {
                System.out.println("接收到過期事件,取消訂單:" + message);
                // 執(zhí)行取消訂單的業(yè)務(wù)邏輯
            }
        }
    }, "__keyevent@0__:expired"); // 訂閱過期事件
}

優(yōu)點:

  • 實現(xiàn)簡單,直接利用 Redis 的過期機(jī)制。
  • 實時性高,過期事件觸發(fā)后立即響應(yīng)。

缺點:

  • 依賴 Redis 的事件通知功能,需要開啟 notify-keyspace-events 配置。
  • 如果 Redis 中大量使用過期 Key,可能導(dǎo)致性能問題。

注意事項:要使用 Key 過期事件,需要確保 Redis 配置文件中 notify-keyspace-events 的值包含 Ex。比如:

notify-keyspace-events Ex

5. 基于消息隊列(如RabbitMQ)

適用場景:高并發(fā)系統(tǒng),實時性要求高。

訂單創(chuàng)建時,將訂單消息發(fā)送到延遲隊列(如RabbitMQ 的 x-delayed-message 插件)。

延遲時間到了以后,消息會重新投遞到消費者,消費者執(zhí)行取消操作。

示例代碼(以RabbitMQ為例):

public void sendOrderToDelayQueue(String orderId, long delay) {
    Map<String, Object> args = new HashMap<>();
    args.put("x-delayed-type", "direct");
    ConnectionFactory factory = new ConnectionFactory();
    try (Connection connection = factory.newConnection();
         Channel channel = connection.createChannel()) {
        channel.exchangeDeclare("delayed_exchange", "x-delayed-message", true, false, args);
        channel.queueDeclare("delay_queue", true, false, false, null);
        channel.queueBind("delay_queue", "delayed_exchange", "order.cancel");

        AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
                .headers(Map.of("x-delay", delay)) // 延遲時間
                .build();
        channel.basicPublish("delayed_exchange", "order.cancel", props, orderId.getBytes());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

優(yōu)點:

  • 消息隊列支持分布式,高并發(fā)下表現(xiàn)優(yōu)秀。
  • 數(shù)據(jù)可靠性高,不容易丟消息。

缺點:

  • 引入消息隊列增加了系統(tǒng)復(fù)雜性。
  • 需要處理隊列堆積的問題。

6. 使用定時任務(wù)框架

適用場景:訂單取消操作復(fù)雜,需要分布式支持。

定時任務(wù)框架,比如:Quartz、Elastic-Job,能夠高效地管理任務(wù)調(diào)度,適合處理批量任務(wù)。

比如 Quartz 可以通過配置 Cron 表達(dá)式,定時執(zhí)行訂單取消邏輯。

示例代碼:

@Scheduled(cron = "0 */5 * * * ?")
public void scanAndCancelOrders() {
    System.out.println("開始掃描并取消過期訂單");
    // 這里調(diào)用數(shù)據(jù)庫更新邏輯
}

優(yōu)點:

  • 成熟的調(diào)度框架支持復(fù)雜任務(wù)調(diào)度。
  • 靈活性高,支持分布式擴(kuò)展。

缺點:

  • 對實時性支持有限。
  • 框架本身較復(fù)雜。

7. 基于觸發(fā)式事件流處理

適用場景:需要處理實時性較高的訂單取消,同時結(jié)合復(fù)雜業(yè)務(wù)邏輯,例如根據(jù)用戶行為動態(tài)調(diào)整超時時間。

可以借助事件流處理框架(如 Apache Flink 或 Spark Streaming),實時地處理訂單狀態(tài),并觸發(fā)超時事件。

每個訂單生成后,可以作為事件流的一部分,訂單未支付時通過流計算觸發(fā)超時取消邏輯。

示例代碼(以 Apache Flink 為例):

DataStream<OrderEvent> orderStream = env.fromCollection(orderEvents);

orderStream
    .keyBy(OrderEvent::getOrderId)
    .process(new KeyedProcessFunction<String, OrderEvent, Void>() {
        @Override
        public void processElement(OrderEvent event, Context ctx, Collector<Void> out) throws Exception {
            // 注冊一個定時器
            ctx.timerService().registerProcessingTimeTimer(event.getTimestamp() + 30000); // 30秒超時
        }

        @Override
        public void onTimer(long timestamp, OnTimerContext ctx, Collector<Void> out) throws Exception {
            // 定時器觸發(fā),執(zhí)行訂單取消邏輯
            System.out.println("訂單超時取消,訂單ID:" + ctx.getCurrentKey());
        }
    });

優(yōu)點:

  • 實時性高,支持復(fù)雜事件處理邏輯。
  • 適合動態(tài)調(diào)整超時時間,滿足靈活的業(yè)務(wù)需求。

缺點:

  • 引入了流計算框架,系統(tǒng)復(fù)雜度增加。
  • 對運維要求較高。

總結(jié)

每種方案都有自己的適用場景,大家在選擇的時候,記得結(jié)合業(yè)務(wù)需求、訂單量、并發(fā)量來綜合考慮。

如果你的項目規(guī)模較小,可以直接用延時隊列或 Redis;而在大型高并發(fā)系統(tǒng)中,消息隊列和事件流處理往往是首選。

當(dāng)然,代碼實現(xiàn)只是第一步,更重要的是在實際部署和運行中進(jìn)行性能調(diào)優(yōu),保證系統(tǒng)的穩(wěn)定性。

希望這篇文章能給大家一些啟發(fā),也歡迎討論其他可能的實現(xiàn)思路!

責(zé)任編輯:武曉燕 來源: 蘇三說技術(shù)
相關(guān)推薦

2023-01-30 08:12:53

訂單超時自動取消延長訂單

2022-12-01 08:25:03

訂單超時定時任務(wù)

2024-02-26 08:50:37

訂單自動取消消息

2022-07-30 10:08:06

MQTT?協(xié)議物聯(lián)網(wǎng)

2023-09-19 15:33:50

Web實時消息推送

2024-08-01 11:38:40

2021-11-29 09:15:57

Github網(wǎng)絡(luò)Python

2022-04-26 05:55:52

網(wǎng)絡(luò)網(wǎng)絡(luò)類型

2025-02-24 16:00:00

SpringBoot定時任務(wù)開發(fā)

2020-01-14 08:00:00

.NET緩存編程語言

2022-05-10 08:08:01

find命令Linux

2010-10-15 10:02:01

Mysql表類型

2022-03-24 12:15:50

MySQL數(shù)據(jù)庫讀寫分離

2025-01-21 08:00:00

限流微服務(wù)算法

2019-04-17 09:00:00

DevOps基礎(chǔ)架構(gòu)代碼工具

2013-01-07 10:14:06

JavaJava枚舉

2019-09-06 09:00:00

開發(fā)技能代碼

2020-01-14 11:09:36

CIO IT技術(shù)

2025-01-15 10:46:23

開發(fā)JavaScript集合

2021-09-07 08:14:26

訂單超時未支付
點贊
收藏

51CTO技術(shù)棧公眾號