偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

訂單 30 分鐘未支付就自動(dòng)取消?這五個(gè)狠招幫你搞定!

開(kāi)發(fā) 前端
訂單 30 分鐘未支付自動(dòng)取消這個(gè)功能,看似簡(jiǎn)單,背后卻有很多技術(shù)細(xì)節(jié)需要考慮。從簡(jiǎn)單的數(shù)據(jù)庫(kù)輪詢到復(fù)雜的分布式定時(shí)器,每種方案都有自己的適用場(chǎng)景和優(yōu)缺點(diǎn)。大家在實(shí)際開(kāi)發(fā)中,要根據(jù)自己的系統(tǒng)情況選擇合適的方案,同時(shí)注意冪等性、事務(wù)處理、性能優(yōu)化和監(jiān)控報(bào)警等問(wèn)題。
兄弟們,不知道大家有沒(méi)有這樣的經(jīng)歷:點(diǎn)外賣(mài)時(shí)選了半天,最后糾結(jié)癥發(fā)作沒(méi)付款,過(guò)一會(huì)兒再看訂單居然自己消失了;網(wǎng)上購(gòu)物時(shí)加入購(gòu)物車(chē)一頓操作,結(jié)果有事耽擱沒(méi)付錢(qián),回來(lái)發(fā)現(xiàn)訂單 “不翼而飛”。其實(shí)這背后藏著一個(gè)重要的技術(shù)需求 —— 訂單 30 分鐘未支付自動(dòng)取消。今天咱就來(lái)聊聊怎么用技術(shù)手段搞定這個(gè)需求,讓你的系統(tǒng)也能像這些大廠一樣智能。

一、先搞明白需求本質(zhì)

咱先不著急上代碼,先把需求掰扯清楚。訂單自動(dòng)取消功能,核心就是在訂單創(chuàng)建后的 30 分鐘內(nèi),如果用戶沒(méi)完成支付,系統(tǒng)就自動(dòng)把這個(gè)訂單關(guān)掉。這里面有幾個(gè)關(guān)鍵點(diǎn)得注意:

  1. 時(shí)間準(zhǔn)確性:必須嚴(yán)格在 30 分鐘后執(zhí)行取消操作,不能早也不能晚,不然用戶體驗(yàn)可就不好了。比如用戶剛付完錢(qián),訂單就被取消了,那不得罵娘。
  2. 可靠性:不管系統(tǒng)是高峰期還是低谷期,都得保證該取消的訂單一定能取消,不能漏掉任何一個(gè)。要是有訂單沒(méi)取消,可能會(huì)導(dǎo)致庫(kù)存錯(cuò)誤、資金結(jié)算異常等問(wèn)題。
  3. 性能影響:不能因?yàn)檫@個(gè)功能把系統(tǒng)搞得卡頓,尤其是在訂單量大的時(shí)候,得考慮如何高效地處理這些定時(shí)任務(wù)。

二、五大狠招逐個(gè)解析

狠招一:數(shù)據(jù)庫(kù)輪詢 —— 簡(jiǎn)單直接但有點(diǎn)笨的辦法

這是最容易想到的辦法,就像班主任盯著全班學(xué)生抄作業(yè)一樣,定時(shí)去數(shù)據(jù)庫(kù)里查一遍所有未支付的訂單,看看有沒(méi)有超過(guò) 30 分鐘的,有的話就取消。

實(shí)現(xiàn)步驟

  1. 建一張訂單表,里面得有訂單狀態(tài)(比如未支付、已支付、已取消)、創(chuàng)建時(shí)間等字段。
  2. 寫(xiě)一個(gè)定時(shí)任務(wù),比如用 Spring 的 @Scheduled 注解,每隔一段時(shí)間(比如 1 分鐘)就去數(shù)據(jù)庫(kù)查詢一次狀態(tài)為未支付且創(chuàng)建時(shí)間超過(guò) 30 分鐘的訂單。
  3. 對(duì)查詢出來(lái)的訂單執(zhí)行取消操作,更新訂單狀態(tài),可能還需要釋放庫(kù)存、發(fā)送通知等。

代碼示例

@Service
public class OrderCancelService {
    @Autowired
    private OrderRepository orderRepository;
    @Scheduled(fixedRate = 60 * 1000) // 每分鐘執(zhí)行一次
    public void cancelUnpaidOrders() {
        Date thirtyMinutesAgo = new Date(System.currentTimeMillis() - 30 * 60 * 1000);
        List<Order> unpaidOrders = orderRepository.findByStatusAndCreateTimeBefore(OrderStatus.UNPAID, thirtyMinutesAgo);
        for (Order order : unpaidOrders) {
            // 執(zhí)行取消邏輯
            order.setStatus(OrderStatus.CANCELED);
            // 釋放庫(kù)存等操作
            releaseStock(order);
            // 發(fā)送通知
            sendCancelNotification(order);
            orderRepository.save(order);
        }
    }
    private void releaseStock(Order order) {
        // 具體庫(kù)存釋放邏輯
    }
    private void sendCancelNotification(Order order) {
        // 發(fā)送短信、APP通知等邏輯
    }
}

優(yōu)缺點(diǎn)分析

  • 優(yōu)點(diǎn):簡(jiǎn)單易懂,不需要引入額外的中間件,對(duì)于小型系統(tǒng)來(lái)說(shuō),快速就能實(shí)現(xiàn)。
  • 缺點(diǎn):太笨了!定時(shí)任務(wù)的間隔不好把握,間隔短了會(huì)頻繁查詢數(shù)據(jù)庫(kù),影響性能;間隔長(zhǎng)了又可能導(dǎo)致訂單取消不及時(shí)。而且如果訂單量很大,每次查詢都可能是全表掃描,數(shù)據(jù)庫(kù)壓力山大,就像讓一個(gè)小學(xué)生去搬一堆磚,累得氣喘吁吁。

狠招二:JDK 自帶定時(shí)器 —— 稍微聰明一點(diǎn)的本地方案

Java 自帶了一個(gè) Timer 類(lèi),可以實(shí)現(xiàn)定時(shí)任務(wù),相當(dāng)于在本地搞了個(gè)小鬧鐘,到時(shí)間就提醒系統(tǒng)去取消訂單。

實(shí)現(xiàn)原理

Timer 類(lèi)可以安排 TimerTask 任務(wù)在指定的時(shí)間執(zhí)行,或者周期性地執(zhí)行。我們可以在訂單創(chuàng)建的時(shí)候,啟動(dòng)一個(gè) Timer,讓它在 30 分鐘后執(zhí)行訂單取消任務(wù)。

實(shí)現(xiàn)步驟

  1. 訂單創(chuàng)建時(shí),獲取訂單的創(chuàng)建時(shí)間,計(jì)算出 30 分鐘后的執(zhí)行時(shí)間。
  2. 創(chuàng)建一個(gè) TimerTask 任務(wù),在 run 方法里實(shí)現(xiàn)訂單取消邏輯。
  3. 通過(guò) Timer 的 schedule 方法,把任務(wù)安排在計(jì)算好的時(shí)間執(zhí)行。

代碼示例

public class OrderService {
    private Timer timer = new Timer("OrderCancelTimer");
    public void createOrder(Order order) {
        // 保存訂單到數(shù)據(jù)庫(kù)
        saveOrder(order);
        // 安排取消任務(wù)
        scheduleCancelTask(order);
    }
    private void scheduleCancelTask(Order order) {
        long delay = 30 * 60 * 1000; // 30分鐘
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                // 檢查訂單狀態(tài),防止重復(fù)取消或已支付的情況
                Order existingOrder = getOrderById(order.getId());
                if (existingOrder.getStatus() == OrderStatus.UNPAID) {
                    cancelOrder(existingOrder);
                }
            }
        };
        timer.schedule(task, delay);
    }
    private void cancelOrder(Order order) {
        // 執(zhí)行取消邏輯,更新數(shù)據(jù)庫(kù)等
    }
}

優(yōu)缺點(diǎn)分析

  • 優(yōu)點(diǎn):比數(shù)據(jù)庫(kù)輪詢更精準(zhǔn),每個(gè)訂單都有自己的 “鬧鐘”,到時(shí)間就執(zhí)行,不會(huì)有延遲。而且是 JDK 自帶的,不需要額外依賴。
  • 缺點(diǎn):局限性很大,只適用于單節(jié)點(diǎn)部署的系統(tǒng)。如果是分布式系統(tǒng),每個(gè)節(jié)點(diǎn)都得自己管理定時(shí)器,任務(wù)無(wú)法共享,容易出現(xiàn)重復(fù)執(zhí)行或者漏執(zhí)行的情況。而且 Timer 線程是非守護(hù)線程,如果程序不關(guān)閉,它會(huì)一直運(yùn)行,可能會(huì)有資源泄漏的問(wèn)題,就像你養(yǎng)了一堆小寵物,卻不管它們,最后家里亂成一團(tuán)。

狠招三:消息隊(duì)列延遲隊(duì)列 —— 分布式場(chǎng)景的好幫手

現(xiàn)在很多系統(tǒng)都是分布式部署的,這時(shí)候就需要一個(gè)分布式的解決方案,延遲隊(duì)列就派上用場(chǎng)了。比如 RabbitMQ 的死信隊(duì)列、RocketMQ 的延遲消息,都可以實(shí)現(xiàn)這個(gè)功能。

以 RabbitMQ 死信隊(duì)列為例

  1. 什么是死信隊(duì)列:死信隊(duì)列就是當(dāng)消息成為死信后,會(huì)被發(fā)送到的那個(gè)隊(duì)列。而消息成為死信的原因有很多,比如消息被拒絕、超時(shí)未消費(fèi)等。我們可以利用消息超時(shí)未消費(fèi)這一點(diǎn)來(lái)實(shí)現(xiàn)延遲隊(duì)列。
  2. 實(shí)現(xiàn)步驟:

創(chuàng)建一個(gè)普通隊(duì)列(死信源隊(duì)列),設(shè)置隊(duì)列的過(guò)期時(shí)間(TTL)為 30 分鐘。

創(chuàng)建一個(gè)死信隊(duì)列,用于接收過(guò)期的消息。

將死信源隊(duì)列和死信隊(duì)列綁定,當(dāng)死信源隊(duì)列中的消息過(guò)期后,會(huì)自動(dòng)轉(zhuǎn)發(fā)到死信隊(duì)列。

消費(fèi)者監(jiān)聽(tīng)死信隊(duì)列,當(dāng)收到消息時(shí),執(zhí)行訂單取消邏輯。

代碼示例(RabbitMQ)

// 配置類(lèi)
@Configuration
public class RabbitMQConfig {
    // 死信源隊(duì)列
    public static final String DEAD_LETTER_QUEUE = "dead_letter_queue";
    // 死信交換器
    public static final String DEAD_LETTER_EXCHANGE = "dead_letter_exchange";
    // 死信路由鍵
    public static final String DEAD_LETTER_ROUTING_KEY = "dead_letter_routing_key";
    // 真正的死信隊(duì)列
    public static final String REAL_DEAD_LETTER_QUEUE = "real_dead_letter_queue";
    @Bean
    public Queue deadLetterQueue() {
        Map<String, Object> arguments = new HashMap<>();
        // 設(shè)置隊(duì)列過(guò)期時(shí)間30分鐘
        arguments.put("x-message-ttl", 30 * 60 * 1000);
        // 設(shè)置死信交換器
        arguments.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);
        // 設(shè)置死信路由鍵
        arguments.put("x-dead-letter-routing-key", DEAD_LETTER_ROUTING_KEY);
        return new Queue(DEAD_LETTER_QUEUE, true, false, false, arguments);
    }
    @Bean
    public Exchange deadLetterExchange() {
        return ExchangeBuilder.directExchange(DEAD_LETTER_EXCHANGE).durable(true).build();
    }
    @Bean
    public Queue realDeadLetterQueue() {
        return new Queue(REAL_DEAD_LETTER_QUEUE, true);
    }
    @Bean
    public Binding binding() {
        return BindingBuilder.bind(realDeadLetterQueue()).to(deadLetterExchange()).with(DEAD_LETTER_ROUTING_KEY).noargs();
    }
}
// 生產(chǎn)者,訂單創(chuàng)建時(shí)發(fā)送消息到死信源隊(duì)列
@Service
public class OrderProducer {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    public void sendOrderToDeadLetterQueue(Order order) {
        // 將訂單信息轉(zhuǎn)換為JSON
        String orderJson = JSON.toJSONString(order);
        rabbitTemplate.convertAndSend(RabbitMQConfig.DEAD_LETTER_EXCHANGE, RabbitMQConfig.DEAD_LETTER_ROUTING_KEY, orderJson);
    }
}
// 消費(fèi)者,監(jiān)聽(tīng)真正的死信隊(duì)列
@Service
public class OrderConsumer {
    @RabbitListener(queues = RabbitMQConfig.REAL_DEAD_LETTER_QUEUE)
    public void handleDeadLetterMessage(String orderJson) {
        Order order = JSON.parseObject(orderJson, Order.class);
        // 執(zhí)行訂單取消邏輯
        cancelOrder(order);
    }
}

優(yōu)缺點(diǎn)分析

  • 優(yōu)點(diǎn):適合分布式系統(tǒng),解耦了訂單創(chuàng)建和訂單取消邏輯,消息隊(duì)列可以承載大量的延遲任務(wù),性能較好。而且通過(guò)設(shè)置 TTL,能比較準(zhǔn)確地控制延遲時(shí)間。
  • 缺點(diǎn):不同的消息隊(duì)列實(shí)現(xiàn)方式略有不同,比如 RabbitMQ 的 TTL 是隊(duì)列級(jí)別的,一旦設(shè)置,隊(duì)列里所有消息的過(guò)期時(shí)間都一樣,不夠靈活;RocketMQ 雖然支持不同的延遲級(jí)別,但需要提前配置好,不能動(dòng)態(tài)設(shè)置延遲時(shí)間。而且引入消息隊(duì)列會(huì)增加系統(tǒng)的復(fù)雜度,需要處理消息的可靠性、重復(fù)消費(fèi)等問(wèn)題,就像找了個(gè)幫手,但這個(gè)幫手有時(shí)候也會(huì)鬧點(diǎn)小脾氣,得花時(shí)間磨合。

狠招四:分布式定時(shí)器 —— 專業(yè)的定時(shí)任務(wù)框架

如果系統(tǒng)是分布式的,而且定時(shí)任務(wù)很多,要求也比較高,那就可以用專業(yè)的分布式定時(shí)器框架,比如 Quartz、Elastic-Job、XXL-JOB 等。這里以 Quartz 為例來(lái)看看。

Quartz 實(shí)現(xiàn)原理

Quartz 是一個(gè)功能強(qiáng)大的開(kāi)源作業(yè)調(diào)度框架,支持分布式部署。它有一個(gè)調(diào)度器(Scheduler),可以管理多個(gè)作業(yè)(Job)和觸發(fā)器(Trigger)。觸發(fā)器可以設(shè)置觸發(fā)時(shí)間,比如在指定時(shí)間執(zhí)行一次,或者周期性執(zhí)行。作業(yè)就是具體要執(zhí)行的任務(wù)。

實(shí)現(xiàn)步驟

  1. 引入 Quartz 依賴。
  2. 創(chuàng)建 Job 類(lèi),實(shí)現(xiàn)具體的訂單取消邏輯。
  3. 在訂單創(chuàng)建時(shí),創(chuàng)建 Trigger 和 JobDetail,設(shè)置觸發(fā)時(shí)間為訂單創(chuàng)建時(shí)間 + 30 分鐘,然后將它們注冊(cè)到 Scheduler 中。
  4. 配置 Quartz 的集群,確保在分布式環(huán)境下任務(wù)不會(huì)重復(fù)執(zhí)行。

代碼示例

// Job類(lèi)
public class OrderCancelJob implements Job {
    @Autowired
    private OrderService orderService;
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        String orderId = dataMap.getString("orderId");
        orderService.cancelOrder(orderId);
    }
}
// 訂單創(chuàng)建時(shí)調(diào)度任務(wù)
public class OrderService {
    @Autowired
    private Scheduler scheduler;
    public void createOrder(Order order) {
        // 保存訂單
        saveOrder(order);
        // 調(diào)度取消任務(wù)
        scheduleCancelJob(order);
    }
    private void scheduleCancelJob(Order order) throws SchedulerException {
        // 創(chuàng)建JobDetail
        JobDetail jobDetail = JobBuilder.newJob(OrderCancelJob.class)
               .withIdentity("orderCancelJob_" + order.getId(), "orderCancelGroup")
               .usingJobData("orderId", order.getId().toString())
               .build();
        // 創(chuàng)建Trigger,30分鐘后觸發(fā)
        Trigger trigger = TriggerBuilder.newTrigger()
               .withIdentity("orderCancelTrigger_" + order.getId(), "orderCancelGroup")
               .startAt(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
               .build();
        // 將Job和Trigger注冊(cè)到Scheduler
        scheduler.scheduleJob(jobDetail, trigger);
    }
}
// Quartz集群配置(application.properties)
# Quartz配置
quartz.job-store-type=jdbc
quartz.data-source=quartzDataSource
quartz.jdbc.driver=com.mysql.cj.jdbc.Driver
quartz.jdbc.url=jdbc:mysql://localhost:3306/quartz_db?useUnicode=true&characterEncoding=utf-8
quartz.jdbc.user=root
quartz.jdbc.password=123456
# 啟用集群
quartz.scheduler.instanceName=ClusterScheduler
quartz.scheduler.instanceId=AUTO
quartz.job-store-is-clustered=true

優(yōu)缺點(diǎn)分析

  • 優(yōu)點(diǎn):功能強(qiáng)大,支持分布式集群,任務(wù)調(diào)度精準(zhǔn),可配置性高,能處理大量的定時(shí)任務(wù)。而且有豐富的監(jiān)聽(tīng)器和管理接口,方便監(jiān)控和管理。
  • 缺點(diǎn):引入 Quartz 框架需要學(xué)習(xí)成本,配置相對(duì)復(fù)雜,尤其是集群環(huán)境下的配置。而且如果任務(wù)量非常大,數(shù)據(jù)庫(kù)中存儲(chǔ)的 Trigger 和 Job 數(shù)據(jù)會(huì)越來(lái)越多,需要定期清理,否則會(huì)影響性能,就像家里東西太多不收拾,最后找東西都難。

狠招五:Redis 過(guò)期監(jiān)聽(tīng) —— 利用緩存特性實(shí)現(xiàn)

Redis 是一個(gè)高性能的鍵值對(duì)數(shù)據(jù)庫(kù),它支持為鍵設(shè)置過(guò)期時(shí)間,當(dāng)鍵過(guò)期時(shí),可以通過(guò)發(fā)布訂閱模式通知客戶端。我們可以利用這個(gè)特性來(lái)實(shí)現(xiàn)訂單的自動(dòng)取消。

實(shí)現(xiàn)原理

  1. 在訂單創(chuàng)建時(shí),將訂單信息存儲(chǔ)到 Redis 中,并設(shè)置 30 分鐘的過(guò)期時(shí)間。
  2. 開(kāi)啟 Redis 的過(guò)期鍵通知功能,當(dāng)訂單鍵過(guò)期時(shí),Redis 會(huì)發(fā)布一個(gè)事件。
  3. 客戶端監(jiān)聽(tīng)這個(gè)事件,收到事件后,執(zhí)行訂單取消邏輯。

實(shí)現(xiàn)步驟

  • 配置 Redis,開(kāi)啟過(guò)期鍵通知。在 redis.conf 中設(shè)置:
notify-keyspace-events Ex

然后重啟 Redis 服務(wù)。

  • 訂單創(chuàng)建時(shí),將訂單 ID 作為鍵,存儲(chǔ)到 Redis 中,設(shè)置過(guò)期時(shí)間 30 分鐘??梢赃x擇是否存儲(chǔ)訂單的其他信息,也可以只存儲(chǔ)訂單 ID,取消時(shí)再?gòu)臄?shù)據(jù)庫(kù)查詢?cè)敿?xì)信息。
  • 使用 Redis 的發(fā)布訂閱功能,創(chuàng)建一個(gè)監(jiān)聽(tīng)器,監(jiān)聽(tīng)鍵過(guò)期事件。
  • 監(jiān)聽(tīng)器收到事件后,獲取訂單 ID,執(zhí)行取消邏輯。

代碼示例(Spring Boot 集成 Redis)

// 訂單創(chuàng)建時(shí)存儲(chǔ)到Redis
@Service
public class OrderService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    public void createOrder(Order order) {
        // 保存訂單到數(shù)據(jù)庫(kù)
        saveOrderToDatabase(order);
        // 將訂單ID存儲(chǔ)到Redis,設(shè)置30分鐘過(guò)期
        stringRedisTemplate.opsForValue().set("order:unpaid:" + order.getId(), "1", 30, TimeUnit.MINUTES);
    }
}
// Redis監(jiān)聽(tīng)器
@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
    private final ApplicationEventPublisher applicationEventPublisher;
    public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer, ApplicationEventPublisher applicationEventPublisher) {
        super(listenerContainer);
        this.applicationEventPublisher = applicationEventPublisher;
    }
    @Override
    public void onMessage(Message message, byte[] pattern) {
        // 獲取過(guò)期的鍵
        String expiredKey = message.toString();
        if (expiredKey.startsWith("order:unpaid:")) {
            String orderId = expiredKey.split(":")[2];
            // 發(fā)布訂單過(guò)期事件
            applicationEventPublisher.publishEvent(new OrderExpiredEvent(this, orderId));
        }
    }
}
// 訂單過(guò)期事件處理
@Service
public class OrderExpiredEventHandler {
    @EventListener
    public void handleOrderExpiredEvent(OrderExpiredEvent event) {
        String orderId = event.getOrderId();
        // 查詢訂單狀態(tài),防止已支付的情況
        Order order = getOrderFromDatabase(orderId);
        if (order.getStatus() == OrderStatus.UNPAID) {
            cancelOrder(order);
        }
    }
}

優(yōu)缺點(diǎn)分析

  • 優(yōu)點(diǎn):利用 Redis 的高性能和過(guò)期通知特性,能快速處理大量的訂單過(guò)期事件,適用于高并發(fā)場(chǎng)景。而且實(shí)現(xiàn)相對(duì)簡(jiǎn)單,不需要引入復(fù)雜的框架,只需要依賴 Redis 即可。
  • 缺點(diǎn):Redis 的過(guò)期通知不是實(shí)時(shí)的,可能會(huì)有一定的延遲,因?yàn)?Redis 是單線程處理,只有在處理到過(guò)期鍵時(shí)才會(huì)發(fā)布事件。另外,如果系統(tǒng)對(duì) Redis 的依賴很強(qiáng),一旦 Redis 出現(xiàn)故障,可能會(huì)影響訂單取消功能,需要做好容災(zāi)處理,就像你太依賴一個(gè)朋友,他要是生病了,你可能就麻煩了。

三、五大方案對(duì)比與選擇建議

方案

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

缺點(diǎn)

適用場(chǎng)景

數(shù)據(jù)庫(kù)輪詢

簡(jiǎn)單易實(shí)現(xiàn),無(wú)需額外依賴

性能差,實(shí)時(shí)性低,訂單量大時(shí)壓力大

小型系統(tǒng),訂單量少

JDK 自帶定時(shí)器

精準(zhǔn),單節(jié)點(diǎn)適用

分布式支持差,資源管理麻煩

單節(jié)點(diǎn)應(yīng)用,定時(shí)任務(wù)少

消息隊(duì)列延遲隊(duì)列

分布式支持好,解耦度高

不同 MQ 實(shí)現(xiàn)有局限,復(fù)雜度增加

分布式系統(tǒng),對(duì)解耦有要求

分布式定時(shí)器

功能強(qiáng)大,支持集群,可配置性高

學(xué)習(xí)成本高,配置復(fù)雜

大型分布式系統(tǒng),定時(shí)任務(wù)多

Redis 過(guò)期監(jiān)聽(tīng)

高性能,適合高并發(fā)

通知有延遲,依賴 Redis

高并發(fā)場(chǎng)景,對(duì)實(shí)時(shí)性要求不是極高

選擇的時(shí)候可以根據(jù)自己的系統(tǒng)規(guī)模、并發(fā)量、架構(gòu)復(fù)雜度來(lái)決定。如果是小型系統(tǒng),訂單量不大,用數(shù)據(jù)庫(kù)輪詢或者 JDK 定時(shí)器就能搞定;要是分布式系統(tǒng),訂單量中等,消息隊(duì)列延遲隊(duì)列是個(gè)不錯(cuò)的選擇;如果是大型分布式系統(tǒng),定時(shí)任務(wù)很多,對(duì)可靠性和功能要求高,那就選分布式定時(shí)器;要是高并發(fā)場(chǎng)景,Redis 過(guò)期監(jiān)聽(tīng)則更合適。

四、踩坑指南

  1. 冪等性問(wèn)題:不管用哪種方案,都要保證訂單取消操作是冪等的,也就是多次執(zhí)行和執(zhí)行一次的結(jié)果是一樣的。比如在取消訂單前,先檢查訂單狀態(tài),只有未支付的訂單才取消,防止重復(fù)取消已支付或已取消的訂單。
  2. 事務(wù)處理:在執(zhí)行訂單取消時(shí),涉及到更新訂單狀態(tài)、釋放庫(kù)存、通知用戶等操作,要保證這些操作要么全部成功,要么全部失敗,避免出現(xiàn)部分成功的情況。
  3. 性能優(yōu)化:對(duì)于數(shù)據(jù)庫(kù)輪詢和分布式定時(shí)器等方案,要做好數(shù)據(jù)庫(kù)索引優(yōu)化,避免全表掃描;對(duì)于消息隊(duì)列和 Redis 方案,要合理設(shè)置過(guò)期時(shí)間和隊(duì)列參數(shù),提高系統(tǒng)吞吐量。
  4. 監(jiān)控與報(bào)警:一定要對(duì)訂單取消功能進(jìn)行監(jiān)控,比如統(tǒng)計(jì)每分鐘取消的訂單量、失敗的訂單量等,一旦出現(xiàn)異常,及時(shí)報(bào)警處理,避免大量訂單未取消的情況發(fā)生。

五、總結(jié)

訂單 30 分鐘未支付自動(dòng)取消這個(gè)功能,看似簡(jiǎn)單,背后卻有很多技術(shù)細(xì)節(jié)需要考慮。從簡(jiǎn)單的數(shù)據(jù)庫(kù)輪詢到復(fù)雜的分布式定時(shí)器,每種方案都有自己的適用場(chǎng)景和優(yōu)缺點(diǎn)。大家在實(shí)際開(kāi)發(fā)中,要根據(jù)自己的系統(tǒng)情況選擇合適的方案,同時(shí)注意冪等性、事務(wù)處理、性能優(yōu)化和監(jiān)控報(bào)警等問(wèn)題。

責(zé)任編輯:武曉燕 來(lái)源: 石杉的架構(gòu)筆記
相關(guān)推薦

2024-08-27 13:43:38

Spring系統(tǒng)業(yè)務(wù)

2023-10-09 16:35:19

方案Spring支付

2021-09-07 08:14:26

訂單超時(shí)未支付

2020-10-21 09:25:01

互聯(lián)網(wǎng)訂單自動(dòng)關(guān)閉

2023-11-27 08:15:26

Spring訂單取消

2023-11-20 08:39:24

Spring定時(shí)任務(wù)

2021-01-18 11:41:22

SQL數(shù)據(jù)庫(kù)編程語(yǔ)言

2017-10-12 13:50:49

大數(shù)據(jù)企業(yè)分析專家

2024-02-26 08:50:37

訂單自動(dòng)取消消息

2025-03-18 10:25:59

2016-08-24 11:46:28

移動(dòng)應(yīng)用DevOps私有云

2017-10-20 23:50:36

大數(shù)據(jù)數(shù)據(jù)分析企業(yè)

2025-07-01 07:54:00

2023-01-30 08:12:53

訂單超時(shí)自動(dòng)取消延長(zhǎng)訂單

2022-12-01 08:25:03

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

2017-09-26 10:51:55

提高數(shù)據(jù)庫(kù)性能

2021-12-01 06:50:50

Docker底層原理

2024-03-28 08:32:10

美團(tuán)關(guān)閉訂單輪訓(xùn)

2016-04-06 11:14:48

iOS相機(jī)自定義
點(diǎn)贊
收藏

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