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

Redisson延遲隊(duì)列實(shí)現(xiàn)訂單關(guān)閉的操作方法

 更新時(shí)間:2026年03月05日 09:17:44   作者:皮卡丘學(xué)了沒  
Redisson的RDelayedQueue是實(shí)現(xiàn)訂單到期關(guān)閉的利器,它比定時(shí)任務(wù)更精準(zhǔn)、比Redis過期監(jiān)聽更可靠,下面一步步給大家介紹Redisson延遲隊(duì)列實(shí)現(xiàn)訂單關(guān)閉功能,感興趣的朋友一起看看吧

Redisson的RDelayedQueue是實(shí)現(xiàn)訂單到期關(guān)閉的利器,它比定時(shí)任務(wù)更精準(zhǔn)、比Redis過期監(jiān)聽更可靠。其核心原理是利用Redis的**有序集合(Sorted Set)發(fā)布/訂閱(Pub/Sub)**功能,在分布式環(huán)境下可靠地執(zhí)行延遲任務(wù)。

接下來,我們結(jié)合Java代碼,一步步來看如何實(shí)現(xiàn)。

?? 生產(chǎn)端:訂單創(chuàng)建時(shí),埋下“定時(shí)炸彈”

在用戶下單成功后,我們需要做的不是立即啟動(dòng)一個(gè)計(jì)時(shí)器,而是將這筆訂單的ID作為一條“延遲消息”發(fā)送出去。

import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Service
public class OrderService {
    @Resource
    private RedissonClient redissonClient;
    // 隊(duì)列的名稱,可以理解為用于存放待關(guān)閉訂單的“信箱”
    private static final String ORDER_QUEUE_KEY = "order-close-queue";
    public void createOrder(Order order) {
        // 1. 保存訂單到數(shù)據(jù)庫(kù) (省略具體邏輯)
        saveToDB(order);
        log.info("訂單 [{}] 創(chuàng)建成功,等待支付...", order.getId());
        // 2. 將訂單ID放入延遲隊(duì)列,設(shè)置30分鐘后“爆炸”
        try {
            // 獲取一個(gè)阻塞隊(duì)列,這是消費(fèi)者最終要監(jiān)聽的目標(biāo)隊(duì)列
            RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue(ORDER_QUEUE_KEY);
            // 基于阻塞隊(duì)列,創(chuàng)建一個(gè)延遲隊(duì)列
            RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(blockingQueue);
            // 將訂單ID放入延遲隊(duì)列,延遲30分鐘
            delayedQueue.offer(order.getId().toString(), 30, TimeUnit.MINUTES);
            log.info("訂單 [{}] 已放入延遲隊(duì)列,30分鐘后將自動(dòng)關(guān)閉", order.getId());
        } catch (Exception e) {
            log.error("放入延遲隊(duì)列失敗", e);
            // 這里可以考慮補(bǔ)償機(jī)制,比如記錄日志后由定時(shí)任務(wù)兜底
        }
    }
}

核心邏輯:生產(chǎn)者調(diào)用delayedQueue.offer(),將任務(wù)(訂單ID)和延遲時(shí)間(30分鐘)告訴Redisson。Redisson客戶端會(huì)把這個(gè)任務(wù)連同計(jì)算好的執(zhí)行時(shí)間戳,一起存到Redis的一個(gè)**有序集合(ZSet)**中,并以時(shí)間戳作為排序的分?jǐn)?shù)(score)。

?? 消費(fèi)端:時(shí)刻待命,準(zhǔn)時(shí)“拆彈”

我們需要一個(gè)后臺(tái)任務(wù)一直監(jiān)聽,一旦有訂單到期,立刻執(zhí)行關(guān)閉操作。

import org.redisson.api.RBlockingQueue;
import org.redisson.api.RedissonClient;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class OrderCloseListener implements CommandLineRunner {
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private OrderService orderService; // 注入你的訂單Service
    private static final String ORDER_QUEUE_KEY = "order-close-queue";
    @Override
    public void run(String... args) throws Exception {
        // 程序啟動(dòng)后,在一個(gè)獨(dú)立的線程中監(jiān)聽隊(duì)列
        new Thread(() -> {
            log.info("訂單關(guān)閉監(jiān)聽線程已啟動(dòng),等待到期訂單...");
            // 獲取與生產(chǎn)者相同的阻塞隊(duì)列
            RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue(ORDER_QUEUE_KEY);
            // 注意:這里必須調(diào)用一次getDelayedQueue,目的是在消費(fèi)端也初始化相關(guān)的監(jiān)聽器
            // 雖然不調(diào)用也能工作,但官方推薦調(diào)用以保證可靠性 
            redissonClient.getDelayedQueue(blockingQueue); 
            while (true) {
                try {
                    // 阻塞等待,直到有訂單到期。take()方法會(huì)一直阻塞直到拿到數(shù)據(jù)
                    String orderId = blockingQueue.take();
                    log.info("收到到期訂單ID:{},開始執(zhí)行關(guān)閉操作", orderId);
                    // 執(zhí)行真正的關(guān)單業(yè)務(wù)邏輯
                    orderService.closeExpiredOrder(Long.parseLong(orderId));
                } catch (InterruptedException e) {
                    log.error("監(jiān)聽線程被中斷", e);
                    Thread.currentThread().interrupt();
                    break; // 線程中斷時(shí)退出循環(huán)
                } catch (Exception e) {
                    log.error("處理到期訂單時(shí)發(fā)生錯(cuò)誤", e);
                    // 防止單個(gè)消息處理失敗導(dǎo)致循環(huán)中斷,繼續(xù)監(jiān)聽下一個(gè)
                }
            }
        }, "OrderClose-Listener").start();
    }
}

核心邏輯:消費(fèi)者通過blockingQueue.take()阻塞地從Redis的**目標(biāo)隊(duì)列(List)**中獲取消息。當(dāng)一個(gè)任務(wù)在ZSet中的時(shí)間戳小于當(dāng)前時(shí)間,Redisson的后臺(tái)線程就會(huì)自動(dòng)把它從ZSet移動(dòng)到目標(biāo)List中。這時(shí),take()方法就會(huì)立即返回,拿到訂單ID,執(zhí)行關(guān)閉邏輯。

?? 探秘Redisson的“內(nèi)部時(shí)鐘”機(jī)制

你可能會(huì)好奇,Redisson是如何精準(zhǔn)地將到期任務(wù)從ZSet移到List的?這背后有一個(gè)巧妙的設(shè)計(jì):

Redisson為每個(gè)延遲隊(duì)列啟動(dòng)了一個(gè)后臺(tái)輪詢線程(基于Netty的時(shí)間輪實(shí)現(xiàn))。這個(gè)線程會(huì):

  1. 定期查詢ZSet中分?jǐn)?shù)最?。醋钤绲狡冢┑娜蝿?wù)。
  2. 如果該任務(wù)的到期時(shí)間戳小于當(dāng)前時(shí)間,就把這個(gè)任務(wù)以及所有其他到期的任務(wù),從ZSet和另一個(gè)輔助的List中移除,并推入到消費(fèi)者正在監(jiān)聽的目標(biāo)List中。
  3. 為了提高效率,當(dāng)有新的、更早到期的任務(wù)加入時(shí),Redisson會(huì)通過**發(fā)布/訂閱(Pub/Sub)**功能發(fā)送一個(gè)通知,喚醒輪詢線程立即工作,而不是等到下一個(gè)輪詢周期。

所以,整個(gè)過程就像有一個(gè)精準(zhǔn)的“鬧鐘”在幫你管理這些任務(wù)。

?? 實(shí)戰(zhàn)要點(diǎn)與進(jìn)階思考

  1. 消息可靠性:所有任務(wù)數(shù)據(jù)都存儲(chǔ)在Redis中,即使你的應(yīng)用服務(wù)重啟,已經(jīng)提交但未到期的任務(wù)也不會(huì)丟失。Redis的持久化(RDB/AOF)機(jī)制為數(shù)據(jù)提供了最終保障。
  2. 分布式支持:生產(chǎn)者和消費(fèi)者可以是完全不同的應(yīng)用實(shí)例,只要它們連接同一個(gè)Redis、并使用相同的隊(duì)列名稱(如order-close-queue)即可。多個(gè)消費(fèi)者實(shí)例可以同時(shí)take同一個(gè)隊(duì)列,實(shí)現(xiàn)任務(wù)的負(fù)載均衡。
  3. 時(shí)間精度:Redisson的默認(rèn)輪詢間隔約為5秒,這意味著任務(wù)的觸發(fā)時(shí)間可能會(huì)有最多5秒的誤差。如果業(yè)務(wù)對(duì)時(shí)間精度要求極高,可以通過配置config.setScanInterval(2000)來縮短輪詢間隔(單位為毫秒),但會(huì)略微增加Redis的壓力。
  4. 業(yè)務(wù)兜底:作為一種最佳實(shí)踐,即使有了延遲隊(duì)列,也建議配合一個(gè)低頻的定時(shí)任務(wù)(如每小時(shí)執(zhí)行一次)作為最后的檢查,去掃描那些極少數(shù)可能因?yàn)楦鞣N意外(如Redis故障)而未被關(guān)閉的訂單,確保業(yè)務(wù)邏輯的最終一致性。

到此這篇關(guān)于Redisson延遲隊(duì)列實(shí)現(xiàn)訂單關(guān)閉的操作方法的文章就介紹到這了,更多相關(guān)redisson延遲隊(duì)列訂單關(guān)閉內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring Boot2.x如何自定義Endpoint

    Spring Boot2.x如何自定義Endpoint

    這篇文章主要介紹了Spring Boot2.x如何自定義Endpoint,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • idea下如何設(shè)置項(xiàng)目啟動(dòng)的JVM運(yùn)行內(nèi)存大小

    idea下如何設(shè)置項(xiàng)目啟動(dòng)的JVM運(yùn)行內(nèi)存大小

    這篇文章主要介紹了idea下如何設(shè)置項(xiàng)目啟動(dòng)的JVM運(yùn)行內(nèi)存大小問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 使用Java實(shí)現(xiàn)自動(dòng)調(diào)整Excel行高和列寬

    使用Java實(shí)現(xiàn)自動(dòng)調(diào)整Excel行高和列寬

    在處理 Excel 文件時(shí),自動(dòng)調(diào)整行高和列寬是一項(xiàng)常見的需求,本文將介紹如何使用 Java 來自動(dòng)調(diào)整 Excel 文件中的行高和列寬,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下
    2026-01-01
  • 詳解ZXing-core生成二維碼的方法并解析

    詳解ZXing-core生成二維碼的方法并解析

    本文給大家介紹ZXing-core生成二維碼的方法并解析,主要用到goggle發(fā)布的jar來實(shí)現(xiàn)二維碼功能,對(duì)此文感興趣的朋友一起學(xué)習(xí)吧
    2016-05-05
  • IDEA通過git回滾到某個(gè)提交節(jié)點(diǎn)或某個(gè)版本的操作方法

    IDEA通過git回滾到某個(gè)提交節(jié)點(diǎn)或某個(gè)版本的操作方法

    這篇文章主要介紹了IDEA通過git回滾到某個(gè)提交節(jié)點(diǎn)或某個(gè)版本的方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Mybatis?Plus?中的LambdaQueryWrapper示例詳解

    Mybatis?Plus?中的LambdaQueryWrapper示例詳解

    這篇文章主要介紹了Mybatis?Plus?中的LambdaQueryWrapper,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03
  • Java設(shè)計(jì)模式之模板方法詳解

    Java設(shè)計(jì)模式之模板方法詳解

    模板方法的概念:定義了一個(gè)操作中的算法的骨架,而將部分步驟的實(shí)現(xiàn)在子類中完成。模板方法模式使得子類可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟
    2022-07-07
  • Mybatis常用標(biāo)簽整理

    Mybatis常用標(biāo)簽整理

    日常開發(fā)中,MyBatis中標(biāo)簽有著舉足輕重的重要性,以下是一些MyBatis框架中常見的標(biāo)簽及案例,感興趣的朋友跟隨小編一起看看吧
    2007-02-02
  • JAVA回顧:封裝,繼承,多態(tài)

    JAVA回顧:封裝,繼承,多態(tài)

    這篇文章主要介紹了java封裝繼承多態(tài),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • 使用mybatisPlus的queryWrapper做左聯(lián)接,內(nèi)聯(lián)接方式

    使用mybatisPlus的queryWrapper做左聯(lián)接,內(nèi)聯(lián)接方式

    本文介紹了如何使用Mybatis-Plus的QueryWrapper進(jìn)行SQL查詢,包括左連接、內(nèi)連接等操作,通過示例代碼展示了如何構(gòu)建復(fù)雜的SQL查詢,并將結(jié)果存儲(chǔ)在List對(duì)象中返回,希望給讀者提供參考
    2025-03-03

最新評(píng)論

科尔| 延安市| 阳江市| 固阳县| 婺源县| 湛江市| 仪陇县| 攀枝花市| 陵川县| 惠安县| 乐陵市| 遂平县| 衡山县| 云龙县| 合山市| 莎车县| 宣化县| 比如县| 涪陵区| 丰城市| 翼城县| 隆德县| 米泉市| 武穴市| 石景山区| 改则县| 汶上县| 句容市| 南投县| 昌都县| 兴安盟| 南阳市| 阳曲县| 昌邑市| 贵德县| 贺州市| 连江县| 南岸区| 游戏| 仲巴县| 河津市|