超碰人人人人人,色婷婷综合久久久久中文一区二区,国产-第1页-浮力影院,欧美老妇另类久久久久久

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

電商平臺中訂單未支付過期如何實現(xiàn)自動關(guān)單?

freeflydom
2025年4月8日 9:10 本文熱度 98

日常開發(fā)中,我們經(jīng)常遇到這種業(yè)務場景,如:外賣訂單超 30 分鐘未支付,則自動取訂單;用戶注冊成功 15 分鐘后,發(fā)短信息通知用戶等等。這就是延時任務處理場景。

在電商,支付等系統(tǒng)中,一設都是先創(chuàng)建訂單(支付單),再給用戶一定的時間進行支付,如果沒有按時支付的話,就需要把之前的訂單(支付單)取消掉。這種類以的場景有很多,還有比如到期自動收貨,超時自動退款,下單后自動發(fā)送短信等等都是類似的業(yè)務問題。

定時任務(數(shù)據(jù)庫輪詢)

通過定時任務關(guān)閉訂單,是一種成本很低,實現(xiàn)也很容易的方案。通過簡單的幾行代碼,寫一個定時任務,定期掃描數(shù)據(jù)庫中的訂單,如果時間過期,就將其狀態(tài)更新為關(guān)閉即可。

@Scheduled(cron = "0 0 22 * * ?")
public void pmNotify() {
    this.pmService.todoNotify();
}

優(yōu)點:實現(xiàn)容易,成本低,基本不依賴其他組件。

缺點:

  • 時間可能不夠精確。由于定時任務掃描的間隔是固定的,所以可能造成一些訂單已經(jīng)過期了一段時間才被掃描到,訂單關(guān)閉的時間比正常時間晚一些。
  • 增加了數(shù)據(jù)庫的壓力。隨著訂單的數(shù)量越來越多,掃描的成本也會越來越大,執(zhí)行時間也會被拉長,可能導致某些應該被關(guān)閉的訂單遲遲沒有被關(guān)閉。

總結(jié):采用定時任務的方案比較適合對時間要求不是很敏感,并且數(shù)據(jù)量不太多的業(yè)務場景。

JDK 延遲隊列 DelayQueue

DelayQueue是JDK提供的一個無界隊列,DelayQueue隊列中的元素需要實現(xiàn)Delayed,它只提供了一個方法,就是獲取過期時間。

用戶的訂單生成以后,設置過期時間比如30分鐘,放入定義好的DelayQueue,然后創(chuàng)建一個線程,在線程中通過while(true)不斷的從DelayQueue中獲取過期的數(shù)據(jù)。

優(yōu)點:不依賴任何第三方組件,連數(shù)據(jù)庫也不需要了,實現(xiàn)起來也方便。

缺點:

  • 因為DelayQueue是一個無界隊列,如果放入的訂單過多,會造成JVMOOM。
  • DelayQueue基于JVM內(nèi)存,如果JVM重啟了,那所有數(shù)據(jù)就丟失了。
  • 創(chuàng)建了一個不斷while(true)的線程,占用了cpu資源

總結(jié):DelayQueue適用于數(shù)據(jù)量較小,且丟失也不影響主業(yè)務的場景,比如內(nèi)部系統(tǒng)的一些非重要通知,就算丟失,也不會有太大影響。

redis過期監(jiān)聽

redis 是一個高性能的KV 數(shù)據(jù)庫,除了用作緩存以外,其實還提供了過期監(jiān)聽的功能。在redis.conf 中,配置notify-keyspace-events Ex 即可開啟此功能。然后在代碼中繼承KeyspaceEventMessageListener,實現(xiàn)onMessage 就可以監(jiān)聽過期的數(shù)據(jù)量。

public abstract class KeyspaceEventMessageListener implements MessageListener, InitializingBean, DisposableBean {
    private static final Topic TOPIC_ALL_KEYEVENTS = new PatternTopic("__keyevent@*");
    //...省略部分代碼
    public void init() {
        if (StringUtils.hasText(keyspaceNotificationsConfigParameter)) {
            RedisConnection connection =
                listenerContainer.getConnectionFactory().getConnection();
            try {
                Properties config = connection.getConfig("notify-keyspace-events");
                if (!StringUtils.hasText(config.getProperty("notify-keyspace-events"))) {
                    connection.setConfig("notify-keyspace-events",
                        keyspaceNotificationsConfigParameter);
                }
            } finally {
                connection.close();
            }
        }
        doRegister(listenerContainer);
    }
    protected void doRegister(RedisMessageListenerContainer container) {
        listenerContainer.addMessageListener(this, TOPIC_ALL_KEYEVENTS);
    }
    
    //...省略部分代碼
    @Override
    public void afterPropertiesSet() throws Exception {
        init();
    }
}

通過以上源碼,我們可以發(fā)現(xiàn),其本質(zhì)也是注冊一個listener,利用redis 的發(fā)布訂閱,當key 過期時,發(fā)布過期消息(key)到Channel :keyevent@*:expired 中。

在實際的業(yè)務中,我們可以將訂單的過期時間設置比如30 分鐘,然后放入到redis。30 分鐘之后,就可以消費這個key,然后做一些業(yè)務上的后置動作,比如檢查用戶是否支付。

優(yōu)點: 由于redis 的高性能,所以我們在設置key,或者消費key 時,速度上是可以保證的。

缺點:致命缺陷,不宜使用

在 Redis 官方手冊的keyspace-notifications: timing-of-expired-events中明確指出:

Basically expired events are generated when the Redis server deletes the key and not when the time to live theoretically reaches the value of zero

redis 自動過期的實現(xiàn)方式是:定時任務離線掃描并刪除部分過期鍵;在訪問鍵時惰性檢查是否過期并刪除過期鍵。也就是說,由于redis 的key 過期策略原因,當一個key 過期時,redis 從未保證會在設定的過期時間立即刪除并發(fā)送過期通知,自然我們的監(jiān)聽事件也無法第一時間消費到這個key,所以會存在一定的延遲。實際上,過期通知晚于設定的過期時間數(shù)分鐘的情況也比較常見。

另外,在redis5.0 之前,訂閱發(fā)布中的消息并沒有被持久化,自然也沒有所謂的確認機制。所以一旦消費消息的過程中我們的客戶端發(fā)生了宕機,這條消息就徹底丟失了。

總結(jié):redis 的過期訂閱相比于其他方案沒有太大的優(yōu)勢,在實際生產(chǎn)環(huán)境中,用得相對較少。

Redisson分布式延遲隊列

Redisson是一個基于redis實現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)格,它不僅提供了一系列的分布式的Java常用對象,還提供了許多分布式服務。

Redisson除了提供我們常用的分布式鎖外,還提供了一個分布式延遲隊列RDelayedQueue,他是一種基于zset結(jié)構(gòu)實現(xiàn)的延遲隊列,其實現(xiàn)類是RedissonDelayedQueue。delayqueue 中有一個名為 timeoutSetName 的有序集合,其中元素的 score 為投遞時間戳。delayqueue 會定時使用 zrangebyscore 掃描已到投遞時間的消息,然后把它們移動到就緒消息列表中。

delayqueue 保證 redis 不崩潰的情況下不會丟失消息,在沒有更好的解決方案時不妨一試。

優(yōu)點:使用簡單,并且其實現(xiàn)類中大量使用lua 腳本保證其原子性,不會有并發(fā)重復問題。

缺點:需要依賴redis(如果這算一種缺點的話)。

總結(jié):Redisson 是redis 官方推薦的JAVA 客戶端,提供了很多常用的功能,使用簡單、高效,推薦使用。

RocketMQ 延遲消息

延遲消息:當消息寫入到Broker 后,不會立刻被消費者消費,需要等待指定的時長后才可被消費處理的消息,稱為延時消息。

在訂單創(chuàng)建之后,我們就可以把訂單作為一條消息投遞到rocketmq,并將延遲時間設置為30 分鐘,這樣,30 分鐘后我們定義的consumer 就可以消費到這條消息,然后檢查用戶是否支付了這個訂單。

通過延遲消息,我們就可以將業(yè)務解耦,極大地簡化我們的代碼邏輯。

優(yōu)點:可以使代碼邏輯清晰,系統(tǒng)之間完全解耦,只需關(guān)注生產(chǎn)及消費消息即可。另外其吞吐量極高,最多可以支撐萬億級的數(shù)據(jù)量。

缺點:相對來說,mq 是重量級的組件,引入mq 之后,隨之而來的消息丟失、冪等性問題等都加深了系統(tǒng)的復雜度。

總結(jié):通過mq 進行系統(tǒng)業(yè)務解耦,以及對系統(tǒng)性能削峰填谷已經(jīng)是當前高性能系統(tǒng)的標配。

RabbitMQ 死信隊列

除了RocketMQ 的延遲隊列,RabbitMQ 的死信隊列也可以實現(xiàn)消息延遲功能。

死信(Dead Letter) 是 rabbitmq 提供的一種機制。當一條消息滿足下列條件之一那么它會成為死信:

  • 消息被否定確認(如channel.basicNack) 并且此時requeue 屬性被設置為false。
  • 消息在隊列的存活時間超過設置的TTL時間
  • 消息隊列的消息數(shù)量已經(jīng)超過最大隊列長度

基于這樣的機制,我們可以給消息設置一個ttl,然后故意不消費消息,等消息過期就會進入死信隊列,我們再消費死信隊列即可。通過這樣的方式,就可以達到同RocketMQ 延遲消息一樣的效果。

在 rabbitmq 中創(chuàng)建死信隊列的操作流程大概是:

  • 創(chuàng)建一個交換機作為死信交換機
  • 在業(yè)務隊列中配置 x-dead-letter-exchange 和 x-dead-letter-routing-key,將第一步的交換機設為業(yè)務隊列的死信交換機
  • 在死信交換機上創(chuàng)建隊列,并監(jiān)聽此隊列

死信隊列的設計目的是為了存儲沒有被正常消費的消息,便于排查和重新投遞。死信隊列同樣也沒有對投遞時間做出保證,在第一條消息成為死信之前,后面的消息即使過期也不會投遞為死信。

為了解決這個問題,rabbit 官方推出了延遲投遞插件 rabbitmq-delayed-message-exchange ,推薦使用官方插件來做延時消息。

優(yōu)點:同RocketMQ 一樣,RabbitMQ 同樣可以使業(yè)務解耦,基于其集群的擴展性,也可以實現(xiàn)高可用、高性能的目標。

缺點:死信隊列本質(zhì)還是一個隊列,隊列都是先進先出,如果隊頭的消息過期時間比較長,就會導致后面過期的消息無法得到及時消費,造成消息阻塞。

總結(jié):除了增加系統(tǒng)復雜度之外,死信隊列的阻塞問題也是需要我們重點關(guān)注的。

這里說點題外話,使用 redis 過期監(jiān)聽或者 rabbitmq 死信隊列做延時任務都是以設計者預想之外的方式使用中間件,這種出其不意必自斃的行為通常會存在某些隱患,比如缺乏一致性和可靠性保證,吞吐量較低、資源泄漏等。比較出名的一個事例是很多人使用 redis 的 list 作為消息隊列,以致于最后作者看不下去寫了 disque 并最后演變?yōu)?redis stream。工作中還是盡量不要濫用中間件,用專業(yè)的組件做專業(yè)的事

最佳實踐

實際上,在數(shù)據(jù)庫索引設計良好的情況下,定時掃描數(shù)據(jù)庫中未完成的訂單產(chǎn)生的開銷并沒有想象中那么大。在使用 redisson delayqueue 等定時任務中間件時可以同時使用掃描數(shù)據(jù)庫的方法作為補償機制,避免中間件故障造成任務丟失。

為什么不建議用MQ實現(xiàn)訂單到期關(guān)閉

  1. 時間精度和可靠性問題
  • 延遲隊列的不可預測性:MQ通常為異步通信設計,會受到網(wǎng)絡延遲、隊列長度等多種因素影響,可能無法在確切的時間執(zhí)行消息消費,導致訂單關(guān)閉時間不準確。這對于需要嚴格時間控制的訂單業(yè)務來說是一個重要問題。
  • 消息可靠性:盡管MQ能提供相對可靠的消息投遞,但在極端情況下,消息丟失或重復消費依然可能發(fā)生。這會給訂單的準確關(guān)閉帶來風險,如訂單未關(guān)閉或多次錯誤關(guān)閉。
  1. 系統(tǒng)復雜性增加
  • 為了實現(xiàn)這一功能,系統(tǒng)需要引入并維護消息隊列,例如ActiveMQ、RabbitMQ或Kafka,這增加了系統(tǒng)的復雜性和運維負擔。
  • 性能與負載問題:需要處理大量與訂單相關(guān)的延遲消息,可能會導致MQ系統(tǒng)負載增加,尤其是在高并發(fā)環(huán)境下,這會影響系統(tǒng)整體性能。
  1. 靈活性和擴展性問題
  • 動態(tài)性不足:如果需要調(diào)整訂單關(guān)閉時間,已經(jīng)在隊列中的消息很難修改。這會限制系統(tǒng)靈活適應業(yè)務需求變化的能力。
  • 復雜的業(yè)務邏輯:實現(xiàn)簡單的定時關(guān)閉功能需要復雜的處理邏輯,包括消息的發(fā)送、接收、消費、異常處理等,這會增加業(yè)務流程的復雜性。

并發(fā)口訣:一鎖二判三更新

不管我們使用定時任務還是延遲消息時,不可避免的會遇到并發(fā)執(zhí)行任務的情況 (比如重復消費、調(diào)度重試等)。

當我們執(zhí)行任務時,我們可以按照一鎖二判三更新這個口訣來處理。

  1. 鎖定當前需要處理的訂單。
  2. 判斷訂單是否已經(jīng)更新過對應狀態(tài)了
  3. 如果訂單之前沒有更新過狀態(tài)了,可以更新并完成相關(guān)業(yè)務邏輯,否則本次不能更新,也不能完成業(yè)務邏輯。
  4. 釋放當前訂單的鎖。

兜底意識 + 配置監(jiān)控

雖然我們提到了很多的實現(xiàn)策略,現(xiàn)實實戰(zhàn)時依然容易出現(xiàn)問題,比如不合理的操作導致消息丟失。

因此,我們應該具備兜底意識

假如少量消息丟失,我們可以通過每天凌晨跑一次任務,批量將這些未處理的訂單批量取消。這種兜底行為工程實現(xiàn)簡單,同時對系統(tǒng)影響很小。

還有一點,就是配置監(jiān)控。

筆者曾經(jīng)自研過任務調(diào)度系統(tǒng),應用 A 接入后,從控制臺發(fā)現(xiàn)每隔 2 個小時調(diào)度應用 A 的任務時,經(jīng)常發(fā)生超時,通過分析,發(fā)現(xiàn)應用 A 線程出現(xiàn)了死鎖。

這種問題出現(xiàn)的幾率非常高,因此配置監(jiān)控特別要必要。

對業(yè)務系統(tǒng)來講,監(jiān)控分為兩個層面:系統(tǒng)監(jiān)控業(yè)務監(jiān)控。

  • 系統(tǒng)監(jiān)控

在條件允許的情況下,建議關(guān)注性能監(jiān)控,方法可用性監(jiān)控,方法調(diào)用次數(shù)監(jiān)控這三大類。

性能監(jiān)控

上圖是性能監(jiān)控的示例圖,性能監(jiān)控不同時間段性能分布,實時統(tǒng)計 TP99、TP999 、AVG 、MAX 等維度指標,這也是性能調(diào)優(yōu)的重點關(guān)注對象。

  • 業(yè)務監(jiān)控

業(yè)務監(jiān)控功能是從業(yè)務角度出發(fā),各個應用系統(tǒng)需要從業(yè)務層面進行哪些監(jiān)控,以及提供怎樣的業(yè)務層面的監(jiān)控功能支持業(yè)務相關(guān)的應用系統(tǒng)。

具體就是對業(yè)務數(shù)據(jù),業(yè)務功能進行監(jiān)控,實時收集業(yè)務流程的數(shù)據(jù),并根據(jù)設置的策略對業(yè)務流程中不符合預期的部分進行預警和報警,并對收集到業(yè)務監(jiān)控數(shù)據(jù)進行集中統(tǒng)一的存儲和各種方式進行展示。

比如訂單系統(tǒng)中有一個定時結(jié)算的服務,每兩分鐘執(zhí)行一次。我們可以在定時任務 JOB 中添加埋點,并配置業(yè)務監(jiān)控,假如十分鐘該定時任務沒有執(zhí)行,則發(fā)送郵件,短信給相關(guān)負責人。

擴展

一筆訂單,在取消的那一刻用戶剛好付款了,怎么辦?

這種情況在正常的業(yè)務場景中是有可能出現(xiàn)的,因為訂單都會有定時取消的邏輯,比如10 分鐘或者 15分鐘,而用戶剛好卡在這個時間點進行付款,此時就會出現(xiàn)兩種情況:

  1. 用戶支付成功,支付回調(diào)的那一刻支付單剛好還沒取消,而等回調(diào)結(jié)束,取消支付單的事務提交,支付單取消。此時用戶扣款了,但是對應的權(quán)益或資產(chǎn)沒了。

  1. 用戶支付成功,支付回調(diào)的那一刻支付單已經(jīng)被取消。但此時用戶已經(jīng)扣款,東西卻沒了

可以看到,不論是哪種情況,其實都需要做一定的處理,不然用戶肯定會來投訴!

這種場景無非就是支付單支付成功和取消兩種狀態(tài)的“爭奪”,正常情況下,訂單或者支付單都會有狀態(tài)機的存在,在當前場景簡單來說有以下兩條路徑:

  1. 待支付->支付中->支付成功
  2. 待支付->支付中->已取消

針對情況1,如果是支付回調(diào)取勝,此時的狀態(tài)應該已從 支付中->支付成功

針對情況2,如果是取消支付單取勝,此時的狀態(tài)應該已從 支付中->已取消

所以我們在修改支付單狀態(tài)的時候,基于原始狀態(tài)的判斷,就可以做正常的處理,來看下 SOL應該就很清晰了:

# 支付成功
update pay_info set status = 'paySuccess' where orderNo = '1' and status = 'paying';
# 取消
update pay_info set status = 'cancel' where orderNo = '1' and status = 'paying';

重點就是我們加了 status='paying’這個條件,這就能保證情況只有一個能成功,另一個一定失敗。這種其實就是樂觀鎖的方式

  1. 假設情況1成功了,此時用戶已經(jīng)成功付款,那么狀態(tài)已經(jīng)變?yōu)閜aySucces,取消的SQL必定執(zhí)行失敗,此時就讓它失敗,不需要做任何別的處理。

  1. 假設情況2成功了,此時訂單已被取消,status已經(jīng)變?yōu)?cancel,支付成功的SOL必定執(zhí)行失敗,這種情況下我們就需要做逆向處理,即給用戶退款。訂單被取消,用戶的錢也被原路退回,這種處理也沒任何問題

業(yè)務優(yōu)化

針對訂單超時業(yè)務,這里在業(yè)務上可以做一個小優(yōu)化,你想想,用戶付款前可能有點掙扎,然后在最后一刻終于下定決心進行付款,這時候卻告知被退款了,用戶很可能就不會再下單了。因此我們在頁面上可以限時訂單取消設置計時為 10分鐘,但實際后端是延遲 11 分鐘取消訂單,這樣就能避免這種情況的發(fā)生啦。

Redis 分布式鎖實現(xiàn)

最后除了利用數(shù)據(jù)庫處理,還可以使用分布式鎖,對一筆訂單加鎖也能保證這筆訂單正常的業(yè)務流轉(zhuǎn)。每次進行取消訂單或付款操作時,首先嘗試獲取訂單的分布式鎖,確保只有一個操作能修改訂單狀態(tài)。在分布式系統(tǒng)中,訂單在取消的同時用戶付款的競態(tài)問題可以通過分布式鎖來解決。以下是一個具體的、落地的方案,確保訂單狀態(tài)的可靠性,避免因并發(fā)導致狀態(tài)沖突

訂單取消流程:

  1. 超時觸發(fā)取消訂單
  2. 取消訂單方法中先獲取該訂單的分布式鎖。如果鎖被其他操作持有(如付款),等待或拋出異常
  3. 若成功獲取鎖,檢查訂單狀態(tài)是否已付款:
    • 若訂單未付款,將訂單狀態(tài)更新為“已取消”
    • 若訂單已付款,直接跳過這筆訂單的處理。。
    • 釋放分布式鎖,完成取消流程。

訂單付款流程:

  1. 三方支付成功回調(diào)。
  2. 后端系統(tǒng)接收回調(diào)后,先獲取該訂單的分布式鎖,如果鎖被其他提作持有(如取消),等待或拋出異常(沒有給三方響應成功,三方會重新發(fā)起回調(diào))
  3. 若成功獲取鎖,檢查訂單狀態(tài)是否為“待支付”:
  4. 若訂單狀態(tài)為“待支付”,繼續(xù)執(zhí)行扣款,并將訂單狀態(tài)更新為“已付款”。
    • 若訂單狀態(tài)為“已取消”,則發(fā)起退款,并提示用戶訂單已取消,無法支付。
    • 釋放分布式鎖,完成流程。

?轉(zhuǎn)自https://www.cnblogs.com/seven97-top/p/18810985


該文章在 2025/4/8 9:10:25 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點晴ERP是一款針對中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內(nèi)大量中小企業(yè)的青睞。
點晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運作、調(diào)度、堆場、車隊、財務費用、相關(guān)報表等業(yè)務管理,結(jié)合碼頭的業(yè)務特點,圍繞調(diào)度、堆場作業(yè)而開發(fā)的。集技術(shù)的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點晴WMS倉儲管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質(zhì)期管理,貨位管理,庫位管理,生產(chǎn)管理,WMS管理系統(tǒng),標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved