Go 定時任務調度從入門到實戰
在開發後臺服務時,我們經常需要處理定時任務。例如每天凌晨備份數據、每 5 分鐘檢查服務狀態、每小時發送統計報表...
這些場景都需要可靠的定時任務調度機制。Go 語言就提供了強大的定時任務處理能力,既有標準庫的基礎功能,也有企業級的第三方解決方案。
一、標準庫基礎用法
1. 簡單的單次延遲任務
timer := time.NewTimer(3 * time.Second) // 3秒後觸發
<-timer.C // 等待通道消息
fmt.Println("3秒時間到!")
關鍵點:
-
創建後開始倒計時,到期後向
timer.C
通道發送信號 -
適用於延遲執行或超時控制
-
必須調用
timer.Stop()
防止內存泄漏(使用defer timer.Stop()
)
2. 週期重複任務(Ticker)
ticker := time.NewTicker(1 * time.Minute) // 每分鐘觸發
defer ticker.Stop()
for {
<-ticker.C
fmt.Println("每分鐘執行:", time.Now().Format("11:11:22"))
}
關鍵點:
-
持續向通道發送時間信號
-
使用
select
可實現多任務調度 -
注意在協程中處理避免阻塞主線程
3. 簡單循環方案
for {
doTask() // 開始任務
time.Sleep(20 * time.Minute) // 需要等等20
}
適用場景:快速原型開發
缺點:執行間隔包含任務耗時,不夠精確
二、企業級調度方案(cron 庫)
1. 安裝第三方庫
go get github.com/robfig/cron/v3
2. 基礎示例
c := cron.New()
// 每天7:20執行
c.AddFunc("20 7 * * *", func() {
fmt.Println("發送統計數據...")
})
// 每20秒執行(注意啓用秒級配置)
c.AddFunc("*/20 * * * * *", func() {
fmt.Println("檢測心跳:", time.Now().Format("15:04:05"))
})
c.Start() // 啓動調度器
defer c.Stop() // 程序退出時停止
// 保持主線程運行
select{}
3. Cron 表達式詳解
格式:秒 分 時 日 月 周
常用通配符:
-
*
:匹配所有值 -
,
:指定多個值(15,45
表示 15 和 45 分) -
-
:區間(9-17
表示 9 點到 17 點) -
/
:間隔(*/10
每 10 單位)
常用模式:
-
每天凌晨:
0 0 * * *
-
每週一 9 點:
0 9 * * 1
-
每 15 分鐘:
0 */15 * * * *
三、生產環境最佳實踐
1. 錯誤處理與恢復
c := cron.New(
cron.WithChain(
cron.Recover(cron.DefaultLogger), // 捕獲panic
),
)
c.AddFunc("* * * * *", func() {
defer func() {
if err := recover(); err != nil {
fmt.Println("出現問題了:", err)
}
}()
// 業務代碼
})
2. 分佈式任務調度
// 使用Redis分佈式鎖
func withLock(job func()) func() {
return func() {
lock := redis.NewLock("job-key", 30*time.Second)
if ok, _ := lock.Acquire(); !ok {
return
}
defer lock.Release()
job()
}
}
c.AddFunc("@daily", withLock(dailyReport))
3. 性能優化技巧
// 這裏使用的是異步,防止阻塞
c.AddFunc("@hourly", func() { go heavyTask() })
// 防止任務重複執行
c := cron.New(cron.WithChain(
cron.DelayIfStillRunning(cron.DefaultLogger),
))
// 統計任務執行時間
c.AddFunc("* * * * *", func() {
start := time.Now()
defer func() {
fmt.Println("耗時:", time.Since(start))
}()
// 業務代碼
})
四、完整項目示例
package main
import (
"fmt"
"github.com/robfig/cron/v3"
"time"
)
func main() {
// 初始化調度器
location, _ := time.LoadLocation("Asia/Shanghai")
c := cron.New(cron.WithLocation(location))
// 註冊定時任務
c.AddFunc("0 30 9 * * *", sendMorningReport) // 每天9:30
c.AddFunc("*/10 * * * * *", checkSystemHealth) // 每10秒
// 啓動服務
c.Start()
defer c.Stop()
// 保持主線程運行
fmt.Println("任務開始啓動")
select {}
}
func sendMorningReport() {
fmt.Printf("[%s] 開始發送日報\n", time.Now().Format("2006-01-02 15:04:05"))
}
func checkSystemHealth() {
fmt.Printf("[%s] 系統健康檢查\n", time.Now().Format("15:04:05"))
}
五、需要注意的問題
- 時區問題:
-
默認使用系統時區
-
顯式指定時區:
cron.New(cron.WithLocation(loc))
- 任務執行時間:
-
實際執行時間 = 計劃時間 + 前任務延遲
-
長時間任務應使用
go
啓動新協程
- 資源釋放:
-
必須調用
c.Stop()
釋放資源 -
每個 Timer/Ticker 都要
defer Stop()
總結
Go 語言爲定時任務調度提供了多層級解決方案:
-
簡單需求:標準庫的
Ticker
和Timer
即可滿足 -
複雜調度:
cron
庫支持完整的 Cron 表達式 -
生產環境:結合分佈式鎖、錯誤恢復、異步執行等機制
關鍵優勢在於:
✅ 簡潔直觀的 API 設計
✅ 卓越的併發性能(goroutine 輕量級)
✅ 豐富的企業級功能擴展
Go 做定時任務調度相比其他的語言消耗資源很低且精準度高。很適合做高併發分佈式調度系統,讓我們一起認真學習 Go,每天進步一小步,人生將邁一大步。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/FraMrCekCt3jB-3ou5gd2Q