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

springboot整合redisson實現(xiàn)延時隊列(附倉庫地址)

 更新時間:2024年10月16日 09:28:03   作者:ゞ註﹎錠oo  
延時隊列用于管理需要定時執(zhí)行的任務(wù),對于大數(shù)據(jù)量和高實時性需求,使用延時隊列比定時掃庫更高效,Redisson提供一種高效的延時隊列實現(xiàn)方式,本文就來詳細(xì)的介紹一下,感興趣都可以了解學(xué)習(xí)

應(yīng)用場景

通常在一些需要經(jīng)歷一段時間或者到達(dá)某個指定時間節(jié)點才會執(zhí)行的功能,比如以下這些場景:

  • 訂單超時提醒
  • 收貨自動確認(rèn)
  • 會議提醒
  • 代辦事項提醒

為什么使用延時隊列

對于數(shù)據(jù)量小且實時性要求不高的需求來說,最簡單的方法就是定時掃描數(shù)據(jù)庫。

但是,當(dāng)數(shù)量達(dá)到數(shù)百萬、上千萬級別且時,定時掃庫就顯得非常低效且消耗資源,

甚至有些時間間隔小實時性要求高的情況,上一次掃描還沒結(jié)束,下一次就又開始了,

這時候如果使用延時隊列就會比較合適

延時隊列的幾種方式:

  • Quartz 定時任務(wù)實現(xiàn)掃庫
  • DelayQueue JDK中提供了一組實現(xiàn)延遲隊列的API
  • Redis sorted set
  • Redis 過期鍵監(jiān)聽回調(diào)
  • RabbitMQ 死信隊列
  • RabbitMQ 基于插件實現(xiàn)延遲隊列
  • Wheel 時間輪訓(xùn)算法

Redisson 實現(xiàn)延時隊列

顧名思義 Redis son 就是 Redis 的兒子,舉個栗子先:

1.引入 pom

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>${lastest.version}</version>
</dependency>

2.封裝一個 RedissonQueue 類

@Service
public class RedissonQueue {

    public static final String QUEUE = "delayQueue";

    // 默認(rèn)超時時間,30秒
    public static final Integer DEFAULT_TIMEOUT = 30;

    @Resource
    private RedissonClient redissonClient;

    // 加入任務(wù)并設(shè)置到期時間
    public void offer(String taskId, Integer timeout) {
        RDelayedQueue<String> delayedQueue = delayedQueue();
        delayedQueue.offer(taskId, Objects.isNull(timeout) ? DEFAULT_TIMEOUT : timeout, TimeUnit.SECONDS);
    }

    // 移除任務(wù)
    public void remove(String taskId) {
        RDelayedQueue<String> delayedQueue = delayedQueue();
        delayedQueue.removeIf(messageId -> messageId.equals(taskId));
    }

    // 任務(wù)列表
    public RDelayedQueue<String> delayedQueue() {
        RBlockingDeque<String> blockingDeque = blockingDeque();
        return redissonClient.getDelayedQueue(blockingDeque);
    }

    public RBlockingDeque<String> blockingDeque() {
        return redissonClient.getBlockingDeque(QUEUE);
    }

    public boolean isShutdown() {
        return redissonClient.isShutdown();
    }

    public void shutdown() {
        redissonClient.shutdown();
    }

}

3.交給 Spring 管理

@Slf4j
@Service
public class RedissonService implements ApplicationRunner {

    @Resource
    private RedissonQueue redissonQueue;

    @Resource(name = "threadPoolTaskExecutor")
    private ThreadPoolTaskExecutor executor;

    @Override
    public void run(ApplicationArguments args) {
        RBlockingDeque<String> blockingDeque = redissonQueue.blockingDeque();
        executor.execute(() -> {
            while (true) {
                if (redissonQueue.isShutdown()) {
                    return;
                } else {
                    String messageId = null;
                    try {
                        messageId = blockingDeque.take();
                    } catch (InterruptedException e) {
                        log.warn("RedissonConsumer error:{}", e.getMessage());
                    }
                    if (!Objects.isNull(messageId) && !messageId.isEmpty()) {
                        log.warn("timeout messageId : {}", messageId);
                    }
                }
            }
        });

    }

    // 初始化,啟動服務(wù)就執(zhí)行一次
    @PostConstruct
    public void init() {
        redissonQueue.delayedQueue();
    }

    @PreDestroy
    public void shutdown() {
        redissonQueue.shutdown();
    }

}

4.測試接口

@Operation(summary = "添加任務(wù)", description = "添加任務(wù)")
@PostMapping
public ResponseEntity<?> add(@RequestParam(value = "taskId", required = false) String taskId,
                             @RequestParam(value = "timeout", required = false) Integer timeout) {
    taskId = StringUtils.isEmpty(taskId) ? String.valueOf(snowflake.nextId()) : taskId;
    redissonQueue.offer(taskId, timeout);
    return ResponseEntity.ok().body(redissonQueue.delayedQueue());
}

@Operation(summary = "移除任務(wù)", description = "移除任務(wù)")
@DeleteMapping(value = "/{taskId}")
public ResponseEntity<?> remove(@PathVariable("taskId") String taskId) {
    redissonQueue.remove(taskId);
    return ResponseEntity.ok().body(redissonQueue.delayedQueue());
}

5.測試結(jié)果

添加10個任務(wù)

在這里插入圖片描述

刪除第1個任務(wù)

在這里插入圖片描述

可以看到第一個任務(wù)刪除后沒有被執(zhí)行(沒有設(shè)置到期時間,默認(rèn)為30秒到期)

在這里插入圖片描述

實現(xiàn)原理

  • redisson_delay_queue_timeout:delayQueue,sorted set 數(shù)據(jù)類型,存放所有延遲任務(wù),按延遲任務(wù)的到期時間戳(提交任務(wù)時間戳 +
    延遲時間)排序,所以列表最前面第一個元素就是整個延遲隊列中最早被執(zhí)行的任務(wù)。
  • redisson_delay_queue:delayQueue,list 數(shù)據(jù)類型,也是存放所有任務(wù)。
  • delayQueue,list 數(shù)據(jù)類型,被稱為目標(biāo)隊列,這個里面存放的任務(wù)都是已經(jīng)到延遲時間的,可以被消費者獲取的任務(wù),所以上面示例中
    RBlockingQueue 的 take 方法是從此目標(biāo)隊列中獲取任務(wù)的。
  • redisson_delay_queue_channel:delayQueue,是一個 channel,用來通知客戶端開啟一個延遲任務(wù)
  • 生產(chǎn)者提交任務(wù)時將任務(wù)放到 redisson_delay_queue_timeout:delayQueue 中,提交任務(wù)的時間戳+延遲時間
  • 客戶端會有一個延遲任務(wù),這個延遲任務(wù)會向 Redis Server 發(fā)送一段 lua 腳本,Redis 執(zhí)行 lua 腳本中的命令,此操作是原子性的

lua 腳本主要干兩件事

  • 將到了延遲時間的任務(wù)從 redisson_delay_queue_timeout:delayQueue 中移除,存到 delayQueue 這個目標(biāo)隊列
  • 獲取到 redisson_delay_queue_timeout:delayQueue 中最早到期時間的任務(wù)的到期時間戳,發(fā)布到 redisson_delay_queue_channel:
    delayQueue channel 中

當(dāng)客戶端監(jiān)聽到 redisson_delay_queue_channel:delayQueue 這個 channel 的消息時,會再次提交一個客戶端延遲任務(wù),延遲時間就是消息(最早到期時間任務(wù)的到期時間戳)當(dāng)前時間戳
這個時間其實也就是 redisson_delay_queue_channel:delayQueue 中最早到期時間的任務(wù)的剩余的延遲時間。
一旦時間來到最早到期時間任務(wù)的到期時間戳,redisson_delay_queue_timeout:delayQueue 中最早到期時間的任務(wù)已經(jīng)到期,客戶端的延遲任務(wù)也同時到期,
于是開始執(zhí)行 lua 腳本操作,及時將到期任務(wù)放到目標(biāo)隊列中。然后再次發(fā)布剩余的延遲任務(wù)中最早到期任務(wù)的到期時間戳到 channel
中,
如此循環(huán)運行下去,保證 redisson_delay_queue_timeout:delayQueue 中到期數(shù)據(jù)能及時放到目標(biāo)隊列中。
這里存在一個特殊情況,需要項目啟動時就執(zhí)行一次延時隊列。因為由于沒有客戶端延遲任務(wù)的執(zhí)行,
可能會出現(xiàn) redisson_delay_queue_timeout:delayQueue 隊列中有到期但是沒有被放到目標(biāo)隊列的可能,啟動就執(zhí)行一次是為了保證到期的數(shù)據(jù)能被及時放到目標(biāo)隊列中。

結(jié)論

  • Redisson 方案理論上沒有延遲,但當(dāng)消息數(shù)量劇增,消費者消費緩慢這種情況下,可能會導(dǎo)致延遲任務(wù)消費的延遲。

  • 消息丟失問題 Redisson 方案最大程度上減輕消息丟失的可能性,因為所有任務(wù)都是存在 list 和 sorted set 兩種數(shù)據(jù)類型中,Redis
    有持久化機制。除非整個 redis 集群宕機,可能丟失一小部分?jǐn)?shù)據(jù)。

  • 廣播任務(wù)問題,是不會出現(xiàn)的,因為每個客戶端都是從同一個目標(biāo)隊列中獲取任務(wù)。

Redisson 這種實現(xiàn)方案是比較合適且靠譜的,一般中小型項目建議用 Redisson 實現(xiàn)延遲隊列,規(guī)模較大的項目直接上 MQ。

整合DEMO倉庫地址

到此這篇關(guān)于springboot整合redisson實現(xiàn)延時隊列(附倉庫地址)的文章就介紹到這了,更多相關(guān)springboot redisson延時隊列內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot常見get/post請求參數(shù)處理、參數(shù)注解校驗及參數(shù)自定義注解校驗詳解

    SpringBoot常見get/post請求參數(shù)處理、參數(shù)注解校驗及參數(shù)自定義注解校驗詳解

    這篇文章主要給大家介紹了關(guān)于SpringBoot常見get/post請求參數(shù)處理、參數(shù)注解校驗及參數(shù)自定義注解校驗的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • java.lang.ArrayIndexOutOfBoundsException數(shù)組越界異常問題解決

    java.lang.ArrayIndexOutOfBoundsException數(shù)組越界異常問題解決

    這篇文章主要給大家介紹了關(guān)于java.lang.ArrayIndexOutOfBoundsException數(shù)組越界異常問題解決的相關(guān)資料,數(shù)組越界訪問是一個非常嚴(yán)重的問題,文中通過圖文將解決的辦法介紹的非常詳細(xì),需要的朋友可以參考下
    2024-01-01
  • java實現(xiàn)仿射密碼加密解密

    java實現(xiàn)仿射密碼加密解密

    這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)仿射密碼加密解密的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-03-03
  • 分布式鎖實例教程之防止重復(fù)提交

    分布式鎖實例教程之防止重復(fù)提交

    訂單重復(fù)問題已經(jīng)是老生常談的問題了,下面這篇文章主要給大家介紹了關(guān)于分布式鎖實例教程之防止重復(fù)提交的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-11-11
  • Java使用Catcher捕獲異常的實現(xiàn)

    Java使用Catcher捕獲異常的實現(xiàn)

    本文主要介紹了Java使用Catcher捕獲異常的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • 一文詳解各種ElasticSearch查詢在Java中的實現(xiàn)

    一文詳解各種ElasticSearch查詢在Java中的實現(xiàn)

    Elasticsearch是用Java開發(fā)的,并作為Apache許可條款下的開放源碼發(fā)布,是當(dāng)前流行的企業(yè)級搜索引擎,下面這篇文章主要給大家介紹了關(guān)于各種ElasticSearch查詢在Java中實現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • SpringBoot搭Flowable搞工作流的實現(xiàn)示例

    SpringBoot搭Flowable搞工作流的實現(xiàn)示例

    本文主要介紹了SpringBoot搭Flowable搞工作流的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-09-09
  • Java中使用注解獲取和改變Bean的指定變量值

    Java中使用注解獲取和改變Bean的指定變量值

    Java有時需要通過自定義注解,獲取某Bean的某變量的值,根據(jù)業(yè)務(wù)要求處理數(shù)據(jù),然后再把新值設(shè)置回Bean的同一變量中,這篇文章介紹了使用注解獲取和改變Bean變量值的過程,感興趣想要詳細(xì)了解可以參考下文
    2023-05-05
  • SpringBoot使用Redis緩存的實現(xiàn)方法

    SpringBoot使用Redis緩存的實現(xiàn)方法

    這篇文章主要介紹了SpringBoot使用Redis緩存的實現(xiàn)方法,需要的朋友可以參考下
    2018-02-02
  • java中字符串如何計算字節(jié)長度

    java中字符串如何計算字節(jié)長度

    這篇文章主要介紹了java中字符串如何計算字節(jié)長度,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10

最新評論

普安县| 吉首市| 浦东新区| 罗山县| 互助| 阿拉尔市| 门头沟区| 博白县| 阳春市| 曲阜市| 临邑县| 博白县| 互助| 屯留县| 镇坪县| 体育| 铜梁县| 鄂伦春自治旗| 彭州市| 土默特右旗| 错那县| 抚松县| 沾化县| 星座| 冷水江市| 内江市| 鄂托克前旗| 乌鲁木齐市| 靖江市| 青冈县| 讷河市| 谷城县| 永春县| 黄山市| 栖霞市| 砀山县| 雷波县| 稻城县| 双江| 卓资县| 霍城县|