golang實現延遲隊列(delay queue)

golang實現延遲隊列

1 延遲隊列:郵件提醒、訂單自動取消

延遲隊列:處理需要在未來某個特定時間執行的任務。這些任務被添加到隊列中,并且指定了一個執行時間,只有達到指定的時間點時才能從隊列中取出并執行。
應用場景:

  • 郵件提醒
  • 訂單自動取消(超過多少時間未支付,就取消訂單)
  • 對超時任務的處理等

由于任務的執行是在未來的某個時間點,因此這些任務不會立即執行,而是存儲在隊列中,直到它的預定執行時間才會被執行。

2 實現

2.1 simple簡單版:go自帶的time包實現

思路:

  1. 定義Task結構體,包含
  • ExecuteTime time.Time
  • Job func()
  1. 定義DelayQueue
  • TaskQueue []Task
  • func AddTask
  • func RemoveTask
  • ExecuteTask

這種方案存在的問題:

Go程序重啟時,存儲在slice中的延遲處理任務將全部丟失

完整代碼:

package mainimport ("fmt""time"
)/*
基于go實現延遲隊列
*/
type Task struct {ExecuteTime time.TimeJob         func()
}type DelayQueue struct {Tasks []*Task
}func (d *DelayQueue) AddTask(t *Task) {d.Tasks = append(d.Tasks, t)
}func (d *DelayQueue) RemoveTask() {//FIFO: remove the first task to enqueued.Tasks = d.Tasks[1:]
}func (d *DelayQueue) ExecuteTask() {for len(d.Tasks) > 0 {//dequeue a taskcurrentTask := d.Tasks[0]if time.Now().Before(currentTask.ExecuteTime) {//if the task execution time is not up, waittime.Sleep(currentTask.ExecuteTime.Sub(time.Now()))}//execute the taskcurrentTask.Job()//remove task who has been executedd.RemoveTask()}}func main() {fmt.Println("start delayQueue")delayQueue := &DelayQueue{}firstTask := &Task{ExecuteTime: time.Now().Add(time.Second * 1),Job: func() {fmt.Println("executed task 1 after delay")},}delayQueue.AddTask(firstTask)secondTask := &Task{ExecuteTime: time.Now().Add(time.Second * 7),Job: func() {fmt.Println("executed task 2 after delay")},}delayQueue.AddTask(secondTask)delayQueue.ExecuteTask()fmt.Println("all tasks have been done!!!")
}

效果:
在這里插入圖片描述

2.2 complex持久版:go+redis

為了防止Go重啟后存儲到delayQueue的數據丟失,我們可以將任務持久化到redis中。

思路:

  1. 初始化redis連接
  2. 延遲隊列采用redis的zset(有序集合)實現

前置準備:

# 安裝docker
yum install -y yum-utils
yum-config-manager \--add-repo \https://download.docker.com/linux/centos/docker-ce.repo
yum install docker
systemctl start docker# docker搭建redis
mkdir -p /Users/ziyi2/docker-home/redis
docker run -d --name redis -v /Users/ziyi2/docker-home/redis:/data -p 6379:6379 redis

完整代碼:

package mainimport ("fmt""github.com/go-redis/redis"log "github.com/ziyifast/log""time"
)/*
基于redis zset實現延遲隊列
*/
var redisdb *redis.Client
var DelayQueueKey = "delay-queue"func initClient() (err error) {redisdb = redis.NewClient(&redis.Options{Addr:     "localhost:6379",Password: "", // not set passwordDB:       0,  //use default db})_, err = redisdb.Ping().Result()if err != nil {log.Errorf("%v", err)return err}return nil
}func main() {err := initClient()if err != nil {log.Errorf("init redis client err: %v", err)return}addTaskToQueue("task1", time.Now().Add(time.Second*3).Unix())addTaskToQueue("task2", time.Now().Add(time.Second*8).Unix())//執行隊列中的任務getAndExecuteTask()
}// executeTime為unix時間戳,作為zset中的score。允許redis按照task應該執行時間來進行排序
func addTaskToQueue(task string, executeTime int64) {err := redisdb.ZAdd(DelayQueueKey, redis.Z{Score:  float64(executeTime),Member: task,}).Err()if err != nil {panic(err)}
}// 從redis中取一個task并執行
func getAndExecuteTask() {for {tasks, err := redisdb.ZRangeByScore(DelayQueueKey, redis.ZRangeBy{Min:    "-inf",Max:    fmt.Sprintf("%d", time.Now().Unix()),Offset: 0,Count:  1,}).Result()if err != nil {time.Sleep(time.Second * 1)continue}//處理任務for _, task := range tasks {fmt.Println("Execute task: ", task)//執行完任務之后用 ZREM 移除該任務redisdb.ZRem(DelayQueueKey, task)}time.Sleep(time.Second * 1)}
}

效果:

redis一直從延遲隊列中取數據,如果處理完一批則睡眠1s

  • 具體根據大家的業務調整,此處主要介紹思路

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/696598.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/696598.shtml
英文地址,請注明出處:http://en.pswp.cn/news/696598.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

智慧驛站_智慧文旅驛站_輕松的驛站智慧公廁_5G智慧公廁驛站_5G模塊化智慧公廁

多功能城市智慧驛站是在智慧城市建設背景下,所涌現的一種創新型社會配套設施。其中,智慧公廁作為城市智慧驛站的重要功能基礎,具備社會配套不可缺少的特點,所以在應用場景上,擁有廣泛的需求和要求。那么,城…

高企認定的官方費用

高新技術企業認定并沒有直接的“官費”,但是在申請高新技術企業認定過程中,企業可能會涉及到一些與政府部門相關的費用,主要包括以下幾種情況: 1.知識產權相關費用:?申請專利、軟件著作權等知識產權時需要向國家知識…

#12解決request中getReader()和getInputStream()只能調用一次的問題

目錄 1、背景 2、解決方案 2.1、自定義HttpServletRequestWrapper 2.2、JsonRequestHeaderParamsHelper 2.3、HttpServletRequestReplacedFilter 2.4、使用 1、背景 當前系統Content-Type為application/json,參數接收方式采用RequestBody和RequestParam&#…

平時積累的FPGA知識點(10)

平時在FPGA群聊等積累的FPGA知識點,第10期: 41 ZYNQ系列芯片的PL中使用PS端送過來的時鐘,這些時鐘名字是自動生成的嗎? 解釋:是的。PS端設置的是ps_clk,用report_clocks查出來的時鐘名變成了clk_fpga_0&a…

vue系列--通過js生成前端水印的方法

此方法開箱即用,在vue項目中import即可。 例如: //在vue組件中 import Watermark from /utils/watermark.js//在methods中 Watermark.set({color:"",text:""})//設置水印Watermark.remove() //刪除水印 const watermark {}const…

Linux篇:進程

一. 前置知識 1.1馮諾依曼體系結構 我們常見的計算機,如筆記本。我們不常見的計算機,如服務器,大部分都遵守馮諾依曼體系 為什么計算機要采用馮諾依曼體系呢? 在計算機出現之前有很多人都提出過計算機體系結構,但最…

時序數據庫TimescaleDB,實戰部署全攻略

📢📢📢📣📣📣 哈嘍!大家好,我是【IT邦德】,江湖人稱jeames007,10余年DBA及大數據工作經驗 一位上進心十足的【大數據領域博主】!😜&am…

C++ Primer 筆記(總結,摘要,概括)——第5章 語句

目錄 5.1 簡單語句 5.2 語句作用域 5.3 條件語句 5.3.1 if語句 5.3.2 switch語句 5.4 迭代語句 5.4.1 while語句 5.4.2 傳統的for語句 5.4.3 范圍for語句 5.4.4 do while語句 5.5 跳轉語句 5.5.1 break語句 5.5.2 continue語句 5.5.3 goto語句 5.6 try語句塊和異常處理 5…

前端常見面試題

我們前端常見面試題涉及多個方面,這篇文章就先簡單把每個方面都舉幾個列子,分別寫一下常見的主題和可能的問題。 一:HTML/CSS 基礎 問題: 1.解釋一下什么是語義化標簽?它的好處是什么? 2.CSS 選擇器的優先級是如何工…

2024華北醫院信息網絡大會第二輪更新通知

大會背景 近年來,我國醫療行業信息化取得了飛躍式的發展,醫療信息化對醫療行業有著重要的支撐作用。2021年國家衛健委、中醫藥管理局聯合印發《公立醫院高質量發展促進行動(2021-2025年)》,提出重點建設“三位一體”智…

【青龍】快速搭建青龍面板,部署屬于你自己的應用!

青龍面板是一個支持 Python3、JavaScript、Shell、Typescript 的定時任務管理平臺。 廢話不多說,直接開始。 這里使用一臺 雨云 的云服務器作為演示。雨云注冊地址:https://www.rainyun.com/ 優惠碼:lz932 使用優惠碼注冊后綁定微信可獲得8折…

【Chrono Engine學習總結】4-vehicle-4.3-兩個vehicle碰撞測試

由于Chrono的官方教程在一些細節方面解釋的并不清楚,自己做了一些嘗試,做學習總結。 今天突發奇想,想試一下,是否可以實現兩個vehicle的碰撞? 1、兩輛vehicle的仿真 官方提供了demo_VEH_TwoCars這個demo&#xff0c…

C++入門04 函數的參數傳遞、引用類型與重載

圖源:文心一言 聽課筆記簡單整理,供小伙伴們參考,包含以下內容“🐋3.11 引用類型、🐋3.14 內聯函數、🐋3.15 默認參數值、🐋3.16 函數重載、🐋3.17 C系統函數”~🥝&…

LabVIEW多通道壓力傳感器實時動態檢測

LabVIEW多通道壓力傳感器實時動態檢測 介紹了一種基于LabVIEW的多通道壓力傳感器實時動態檢測系統,解決壓阻式壓力傳感器溫度補償過程的復雜度,提高測量的準確性。通過自動輪詢檢測方法,結合硬件檢測模型和多通道檢測系統設計,本…

集合框架之List集合

目錄 ?編輯 一、什么是UML 二、集合框架 三、List集合 1.特點 2.遍歷方式 3.刪除 4.優化 四、迭代器原理 五、泛型 六、裝拆箱 七、ArrayList、LinkedList和Vector的區別 ArrayList和Vector的區別 LinkedList和Vector的區別 一、什么是UML UML(Unif…

基于ORB-SLAM2與YOLOv8剔除動態特征點(三種方法)

基于ORB-SLAM2與YOLOv8剔除動態特征點(三種方法) 寫上篇文章時測試過程比較亂,寫的時候有些地方有點失誤,所以重新寫了這篇 本文內容均在RGB-D環境下進行程序測試 本文涉及到的動態特征點剔除速度均是以https://cvg.cit.tum.de/data/datasets/rgbd-dat…

系統學習Python——裝飾器:類裝飾器-[單例類:編寫替代方案]

分類目錄:《系統學習Python》總目錄 有趣的是,這里如果能使用nonlocal語句(僅在Python3.X中可用)來改變外層作用域名稱,我們在這里可以編寫一個自包含程度更高的解決方案一一一下面的替代方案為每個類使用了一個外層作…

編寫程序,實現shell功能——項目訓練——day08

c c今天做了一個實戰項目訓練,編寫一個程序,實現shell功能,我們稱之為minishell。 主要是利用Linux中IO接口實現,實現的功能有: 1.ls ls -a ls -l cd cp mv pwd c…

軟件License授權原理

軟件License授權原理 你知道License是如何防止別人破解的嗎?本文將介紹License的生成原理,理解了License的授權原理你不但可以防止別人破解你的License,你甚至可以研究別人的License找到它們的漏洞。喜歡本文的朋友建議收藏關注,…

【Linux】進程狀態

進程狀態 進程狀態的簡要介紹運行狀態進程排隊 阻塞狀態掛起狀態Linux中的進程狀態 進程狀態的簡要介紹 進程狀態指的是一個操作系統中正在運行的進程當前所處的狀態。根據不同的操作系統,進程狀態可能會有一些細微的差別,但最主要的是以下三種狀態 運行…