教育行業(yè)A股IPO第一股(股票代碼 003032)

全國(guó)咨詢(xún)/投訴熱線:400-618-4000

怎樣為程序添加延遲任務(wù)?延遲任務(wù)實(shí)現(xiàn)思路

更新時(shí)間:2023年06月21日18時(shí)41分 來(lái)源:傳智教育 瀏覽次數(shù):

好口碑IT培訓(xùn)

延遲任務(wù)有固定周期有明確出發(fā)時(shí)間,而延遲隊(duì)列沒(méi)有固定的開(kāi)始時(shí)間它常常是由一個(gè)事件觸發(fā)的,而在這個(gè)事件觸發(fā)之后的一段時(shí)間內(nèi)觸發(fā)另一個(gè)事件,任務(wù)可以立即執(zhí)行,也可以延遲。

延遲任務(wù)

延遲任務(wù)的應(yīng)用場(chǎng)景:

場(chǎng)景一:訂單下單之后30分鐘后,如果用戶(hù)沒(méi)有付錢(qián),則系統(tǒng)自動(dòng)取消訂單;如果期間下單成功,任務(wù)取消

場(chǎng)景二:接口對(duì)接出現(xiàn)網(wǎng)絡(luò)問(wèn)題,1分鐘后重試,如果失敗,2分鐘重試,直到出現(xiàn)閾值終止

實(shí)現(xiàn)延遲任務(wù)的兩種任務(wù)

1)DelayQueue

JDK自帶DelayQueue 是一個(gè)支持延時(shí)獲取元素的阻塞隊(duì)列, 內(nèi)部采用優(yōu)先隊(duì)列 PriorityQueue 存儲(chǔ)元素,同時(shí)元素必須實(shí)現(xiàn) Delayed 接口;在創(chuàng)建元素時(shí)可以指定多久才可以從隊(duì)列中獲取當(dāng)前元素,只有在延遲期滿(mǎn)時(shí)才能從隊(duì)列中提取元素

DelayQueue

DelayQueue屬于排序隊(duì)列,它的特殊之處在于隊(duì)列的元素必須實(shí)現(xiàn)Delayed接口,該接口需要實(shí)現(xiàn)compareTo和getDelay方法

getDelay方法:獲取元素在隊(duì)列中的剩余時(shí)間,只有當(dāng)剩余時(shí)間為0時(shí)元素才可以出隊(duì)列。

compareTo方法:用于排序,確定元素出隊(duì)列的順序。

實(shí)現(xiàn):

1:在測(cè)試包jdk下創(chuàng)建延遲任務(wù)元素對(duì)象DelayedTask,實(shí)現(xiàn)compareTo和getDelay方法,

2:在main方法中創(chuàng)建DelayQueue并向延遲隊(duì)列中添加三個(gè)延遲任務(wù),

3:循環(huán)的從延遲隊(duì)列中拉取任務(wù)

public class DelayedTask  implements Delayed{
    
    // 任務(wù)的執(zhí)行時(shí)間
    private int executeTime = 0;
    
    public DelayedTask(int delay){
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.SECOND,delay);
        this.executeTime = (int)(calendar.getTimeInMillis() /1000 );
    }

    /**
     * 元素在隊(duì)列中的剩余時(shí)間
     * @param unit
     * @return
     */
    @Override
    public long getDelay(TimeUnit unit) {
        Calendar calendar = Calendar.getInstance();
        return executeTime - (calendar.getTimeInMillis()/1000);
    }

    /**
     * 元素排序
     * @param o
     * @return
     */
    @Override
    public int compareTo(Delayed o) {
        long val = this.getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
        return val == 0 ? 0 : ( val < 0 ? -1: 1 );
    }


    public static void main(String[] args) {
        DelayQueue<DelayedTask> queue = new DelayQueue<DelayedTask>();
        
        queue.add(new DelayedTask(5));
        queue.add(new DelayedTask(10));
        queue.add(new DelayedTask(15));

        System.out.println(System.currentTimeMillis()/1000+" start consume ");
        while(queue.size() != 0){
            DelayedTask delayedTask = queue.poll();
            if(delayedTask !=null ){
                System.out.println(System.currentTimeMillis()/1000+" cosume task");
            }
            //每隔一秒消費(fèi)一次
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }     
    }
}

DelayQueue實(shí)現(xiàn)完成之后思考一個(gè)問(wèn)題:

使用線程池或者原生DelayQueue程序掛掉之后,任務(wù)都是放在內(nèi)存,需要考慮未處理消息的丟失帶來(lái)的影響,如何保證數(shù)據(jù)不丟失,需要持久化(磁盤(pán))

2)RabbitMQ實(shí)現(xiàn)延遲任務(wù)

TTL:Time To Live (消息存活時(shí)間)

死信隊(duì)列:Dead Letter Exchange(死信交換機(jī)),當(dāng)消息成為Dead message后,可以重新發(fā)送另一個(gè)交換機(jī)(死信交換機(jī))。

RabbitMQ實(shí)現(xiàn)延遲任務(wù)

3)redis實(shí)現(xiàn)

zset數(shù)據(jù)類(lèi)型的去重有序(分?jǐn)?shù)排序)特點(diǎn)進(jìn)行延遲。例如:時(shí)間戳作為score進(jìn)行排序。

redis實(shí)現(xiàn)

redis實(shí)現(xiàn)延遲任務(wù)的思路

redis延遲任務(wù)的思路

1.為什么任務(wù)需要存儲(chǔ)在數(shù)據(jù)庫(kù)中?

延遲任務(wù)是一個(gè)通用的服務(wù),任何需要延遲得任務(wù)都可以調(diào)用該服務(wù),需要考慮數(shù)據(jù)持久化的問(wèn)題,存儲(chǔ)數(shù)據(jù)庫(kù)中是一種數(shù)據(jù)安全的考慮。

2.為什么redis中使用兩種數(shù)據(jù)類(lèi)型,list和zset?

效率問(wèn)題,算法的時(shí)間復(fù)雜度

3.在添加zset數(shù)據(jù)的時(shí)候,為什么不需要預(yù)加載?

任務(wù)模塊是一個(gè)通用的模塊,項(xiàng)目中任何需要延遲隊(duì)列的地方,都可以調(diào)用這個(gè)接口,要考慮到數(shù)據(jù)量的問(wèn)題,如果數(shù)據(jù)量特別大,為了防止阻塞,只需要把未來(lái)幾分鐘要執(zhí)行的數(shù)據(jù)存入緩存即可。

4)延遲任務(wù)服務(wù)實(shí)現(xiàn)

搭建heima-leadnews-schedule模塊

leadnews-schedule是一個(gè)通用的服務(wù),單獨(dú)創(chuàng)建模塊來(lái)管理任何類(lèi)型的延遲任務(wù)

①:導(dǎo)入資料文件夾下的heima-leadnews-schedule模塊到heima-leadnews-service下,如下圖所示:

延遲任務(wù)實(shí)現(xiàn)

②:添加bootstrap.yml

server:
  port: 51701
spring:
  application:
    name: leadnews-schedule
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.200.130:8848
      config:
        server-addr: 192.168.200.130:8848
        file-extension: yml

③:在nacos中添加對(duì)應(yīng)配置,并添加數(shù)據(jù)庫(kù)及mybatis-plus的配置

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/leadnews_schedule?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: root
# 設(shè)置Mapper接口所對(duì)應(yīng)的XML文件位置,如果你在Mapper接口中有自定義方法,需要進(jìn)行該配置
mybatis-plus:
  mapper-locations: classpath*:mapper/*.xml
  # 設(shè)置別名包掃描路徑,通過(guò)該屬性可以給包中的類(lèi)注冊(cè)別名
  type-aliases-package: com.heima.model.schedule.pojos
0 分享到:
和我們?cè)诰€交談!