最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java實(shí)現(xiàn)訂單超時(shí)自動(dòng)取消的7種方案

 更新時(shí)間:2024年12月24日 09:25:13   作者:蘇三說(shuō)技術(shù)  
在電商、外賣(mài)、票務(wù)等系統(tǒng)中,訂單超時(shí)未支付自動(dòng)取消是一個(gè)常見(jiàn)的需求,這個(gè)功能乍一看很簡(jiǎn)單,甚至很多初學(xué)者會(huì)覺(jué)得:"不就是加個(gè)定時(shí)器么?" 但真到了實(shí)際工作中,細(xì)節(jié)的復(fù)雜程度往往會(huì)超乎預(yù)期,本文給大家介紹了Java實(shí)現(xiàn)訂單超時(shí)自動(dòng)取消的7種方案

前言

在電商、外賣(mài)、票務(wù)等系統(tǒng)中,訂單超時(shí)未支付自動(dòng)取消是一個(gè)常見(jiàn)的需求。

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

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

1. 使用延時(shí)隊(duì)列(DelayQueue)

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

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

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

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

示例代碼:

import java.util.concurrent.*;
 
public class OrderCancelService {
    private static final DelayQueue<OrderTask> delayQueue = new DelayQueue<>();
 
    public static void main(String[] args) throws InterruptedException {
        // 啟動(dòng)消費(fèi)者線(xiàn)程
        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)點(diǎn):

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

缺點(diǎn):

  • 依賴(lài)內(nèi)存,系統(tǒng)重啟會(huì)丟失任務(wù)。

  • 隨著訂單量增加,內(nèi)存占用會(huì)顯著上升。

2. 基于數(shù)據(jù)庫(kù)輪詢(xún)

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

輪詢(xún)是最容易想到的方案:定期掃描數(shù)據(jù)庫(kù),將超時(shí)的訂單狀態(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)點(diǎn):

  • 數(shù)據(jù)可靠性強(qiáng),不依賴(lài)內(nèi)存。

  • 實(shí)現(xiàn)成本低,無(wú)需引入第三方組件。

缺點(diǎn):

  • 頻繁掃描數(shù)據(jù)庫(kù),會(huì)帶來(lái)較大的性能開(kāi)銷(xiāo)。

  • 實(shí)時(shí)性較差(通常定時(shí)任務(wù)間隔為分鐘級(jí)別)。

優(yōu)化建議:

  • 為相關(guān)字段加索引,避免全表掃描。

  • 結(jié)合分表分庫(kù)策略,減少單表壓力。

3. 基于Redis隊(duì)列

適用場(chǎng)景:適合對(duì)實(shí)時(shí)性有要求的中小型項(xiàng)目。

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

我們可以把訂單的超時(shí)時(shí)間作為 Score,訂單 ID 作為 Value 存到 Redis 的 ZSet 中,定時(shí)去取出到期的訂單進(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)點(diǎn):

  1. 實(shí)時(shí)性高。

  2. Redis 的性能優(yōu)秀,延遲小。

缺點(diǎn):

  1. Redis 容量有限,適合中小規(guī)模任務(wù)。

  2. 需要額外處理 Redis 宕機(jī)或數(shù)據(jù)丟失的問(wèn)題。

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

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

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

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

例子:

  1. 設(shè)置訂單的過(guò)期時(shí)間:

public void setOrderWithExpiration(String orderId, long expireSeconds) {
    jedis.setex("order:" + orderId, expireSeconds, "PENDING");
}
  • 訂閱 Redis 的過(guò)期事件:

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("接收到過(guò)期事件,取消訂單:" + message);
                // 執(zhí)行取消訂單的業(yè)務(wù)邏輯
            }
        }
    }, "__keyevent@0__:expired"); // 訂閱過(guò)期事件
}

優(yōu)點(diǎn):

  1. 實(shí)現(xiàn)簡(jiǎn)單,直接利用 Redis 的過(guò)期機(jī)制。

  2. 實(shí)時(shí)性高,過(guò)期事件觸發(fā)后立即響應(yīng)。

缺點(diǎn):

  1. 依賴(lài) Redis 的事件通知功能,需要開(kāi)啟 notify-keyspace-events 配置。

  2. 如果 Redis 中大量使用過(guò)期 Key,可能導(dǎo)致性能問(wèn)題。

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

notify-keyspace-events Ex

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

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

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

延遲時(shí)間到了以后,消息會(huì)重新投遞到消費(fèi)者,消費(fèi)者執(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)) // 延遲時(shí)間
                .build();
        channel.basicPublish("delayed_exchange", "order.cancel", props, orderId.getBytes());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

優(yōu)點(diǎn):

  1. 消息隊(duì)列支持分布式,高并發(fā)下表現(xiàn)優(yōu)秀。

  2. 數(shù)據(jù)可靠性高,不容易丟消息。

缺點(diǎn):

  1. 引入消息隊(duì)列增加了系統(tǒng)復(fù)雜性。

  2. 需要處理隊(duì)列堆積的問(wèn)題。

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

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

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

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

示例代碼:

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

優(yōu)點(diǎn):

  1. 成熟的調(diào)度框架支持復(fù)雜任務(wù)調(diào)度。

  2. 靈活性高,支持分布式擴(kuò)展。

缺點(diǎn):

  1. 對(duì)實(shí)時(shí)性支持有限。

  2. 框架本身較復(fù)雜。

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

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

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

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

示例代碼(以 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 {
            // 注冊(cè)一個(gè)定時(shí)器
            ctx.timerService().registerProcessingTimeTimer(event.getTimestamp() + 30000); // 30秒超時(shí)
        }
 
        @Override
        public void onTimer(long timestamp, OnTimerContext ctx, Collector<Void> out) throws Exception {
            // 定時(shí)器觸發(fā),執(zhí)行訂單取消邏輯
            System.out.println("訂單超時(shí)取消,訂單ID:" + ctx.getCurrentKey());
        }
    });

優(yōu)點(diǎn):

  1. 實(shí)時(shí)性高,支持復(fù)雜事件處理邏輯。

  2. 適合動(dòng)態(tài)調(diào)整超時(shí)時(shí)間,滿(mǎn)足靈活的業(yè)務(wù)需求。

缺點(diǎn):

  1. 引入了流計(jì)算框架,系統(tǒng)復(fù)雜度增加。

  2. 對(duì)運(yùn)維要求較高。

總結(jié)

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

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

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

以上就是Java實(shí)現(xiàn)訂單超時(shí)自動(dòng)取消的7種方案的詳細(xì)內(nèi)容,更多關(guān)于Java訂單超時(shí)自動(dòng)取消的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • idea 開(kāi)發(fā)神器之idea插件匯總

    idea 開(kāi)發(fā)神器之idea插件匯總

    這篇文章主要介紹了idea 開(kāi)發(fā)神器之idea插件匯總,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • java異常處理詳細(xì)介紹及實(shí)例

    java異常處理詳細(xì)介紹及實(shí)例

    這篇文章主要介紹了java異常處理詳細(xì)介紹及實(shí)例的相關(guān)資料,本文對(duì)java異常進(jìn)行了知識(shí)層次的總結(jié),需要的朋友可以參考下
    2017-04-04
  • java去除數(shù)組重復(fù)元素的四種方法

    java去除數(shù)組重復(fù)元素的四種方法

    本文給大家分享四種java去除數(shù)組重復(fù)元素的方法,每種方法通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2021-11-11
  • Spring Boot JDBC 連接數(shù)據(jù)庫(kù)示例

    Spring Boot JDBC 連接數(shù)據(jù)庫(kù)示例

    本篇文章主要介紹了Spring Boot JDBC 連接數(shù)據(jù)庫(kù)示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-02-02
  • 使用Java實(shí)現(xiàn)文件夾的遍歷操作指南

    使用Java實(shí)現(xiàn)文件夾的遍歷操作指南

    網(wǎng)上大多采用java遞歸的方式遍歷文件夾下的文件,這里我不太喜歡遞歸的風(fēng)格,這篇文章主要給大家介紹了關(guān)于使用Java實(shí)現(xiàn)文件夾的遍歷操作的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05
  • MyBatis復(fù)雜對(duì)象處理的兩種方式示例代碼文檔

    MyBatis復(fù)雜對(duì)象處理的兩種方式示例代碼文檔

    本文檔詳細(xì)介紹了如何在MyBatis中處理復(fù)雜對(duì)象的兩種方式:使用MyBatis-PlusTypeHandler處理嵌套對(duì)象(自動(dòng)序列化為JSON存儲(chǔ))和使用關(guān)聯(lián)對(duì)象映射處理多個(gè)字段,文中還涵蓋了自定義Mapper方法、事務(wù)管理和關(guān)聯(lián)數(shù)據(jù)操作的注意事項(xiàng),感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • springboot下載接口限速功能實(shí)現(xiàn)

    springboot下載接口限速功能實(shí)現(xiàn)

    通過(guò)Redis統(tǒng)計(jì)并發(fā)數(shù)動(dòng)態(tài)調(diào)整每個(gè)用戶(hù)帶寬,核心邏輯為每秒讀取并發(fā)送限定數(shù)據(jù)量,防止單用戶(hù)占用過(guò)多資源,確保整體下載均衡且高效,本文給大家介紹springboot下載接口限速功能實(shí)現(xiàn),感興趣的朋友一起看看吧
    2025-06-06
  • Mybatis 一對(duì)多和多對(duì)一關(guān)聯(lián)查詢(xún)問(wèn)題

    Mybatis 一對(duì)多和多對(duì)一關(guān)聯(lián)查詢(xún)問(wèn)題

    這篇文章主要介紹了Mybatis 一對(duì)多和多對(duì)一關(guān)聯(lián)查詢(xún)問(wèn)題,需要的朋友可以參考下
    2017-04-04
  • SpringMVC自定義攔截器實(shí)現(xiàn)過(guò)程詳解

    SpringMVC自定義攔截器實(shí)現(xiàn)過(guò)程詳解

    這篇文章主要介紹了SpringMVC自定義攔截器實(shí)現(xiàn)過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • IISExpress?配置允許外部訪(fǎng)問(wèn)詳細(xì)介紹

    IISExpress?配置允許外部訪(fǎng)問(wèn)詳細(xì)介紹

    這篇文章主要介紹了?IISExpress?配置允許外部訪(fǎng)問(wèn)詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下
    2016-11-11

最新評(píng)論

长岭县| 广汉市| 资中县| 稷山县| 葫芦岛市| 遂宁市| 甘泉县| 黎城县| 怀安县| 安达市| 崇仁县| 郯城县| 南安市| 当涂县| 太原市| 平谷区| 盐津县| 揭阳市| 呼玛县| 昌吉市| 德保县| 温州市| 右玉县| 鹤山市| 卢湾区| 莎车县| 鹤壁市| 平定县| 封丘县| 清水县| 左贡县| 长春市| 江门市| 阜康市| 庆安县| 自贡市| 女性| 蒙阴县| 千阳县| 浠水县| 延安市|