golang实现时间快进功能的作业调度插件库sched的使用

golang实现时间快进功能的作业调度插件库sched的使用

logo

sched是一个具有时间快进功能的Go语言作业调度库。它允许你创建定时任务,并可以模拟时间快进来测试调度行为。

主要特性

  • 支持定时任务调度
  • 可以模拟时间快进,便于测试
  • 轻量级且易于使用

安装

go get github.com/romshark/sched

使用示例

下面是一个完整的使用示例,展示如何创建调度任务并使用时间快进功能:

package main

import (
	"fmt"
	"time"

	"github.com/romshark/sched"
)

func main() {
	// 创建新的调度器实例
	scheduler := sched.New()

	// 定义一个简单的任务
	task := func() {
		fmt.Println("任务执行于:", time.Now().Format("2006-01-02 15:04:05"))
	}

	// 安排一个5秒后执行的任务
	scheduler.Schedule(
		sched.Seconds(5), // 5秒后执行
		sched.NewJob(task).OnErr(func(err error) {
			fmt.Println("任务执行出错:", err)
		}),
	)

	// 安排一个每3秒重复执行的任务
	scheduler.Schedule(
		sched.Every(sched.Seconds(3)), // 每3秒执行一次
		sched.NewJob(task),
	)

	// 模拟时间快进10秒
	fmt.Println("快进时间10秒...")
	scheduler.FastForward(sched.Seconds(10))

	// 关闭调度器
	scheduler.Close()
}

时间快进功能

sched的一个重要特性是能够模拟时间快进,这在测试定时任务时非常有用:

package main

import (
	"fmt"
	"time"

	"github.com/romshark/sched"
)

func main() {
	s := sched.New()

	// 记录任务执行次数
	var count int

	// 安排一个每5秒执行的任务
	s.Schedule(
		sched.Every(sched.Seconds(5)),
		sched.NewJob(func() {
			count++
			fmt.Printf("任务执行 #%d 时间: %s\n", 
				count, 
				time.Now().Format("15:04:05"))
		}),
	)

	// 快进时间1分钟(60秒)
	fmt.Println("快进时间1分钟...")
	s.FastForward(sched.Seconds(60))

	// 预期输出:
	// 任务执行 #1 时间: [当前时间]
	// 任务执行 #2 时间: [当前时间+5秒]
	// ...
	// 任务执行 #12 时间: [当前时间+55秒]

	s.Close()
}

高级用法

你还可以创建更复杂的调度规则:

package main

import (
	"fmt"
	"time"

	"github.com/romshark/sched"
)

func main() {
	s := sched.New()

	// 在特定时间执行任务
	s.Schedule(
		sched.NewExecutionTime(
			time.Now().Add(time.Minute*30), // 30分钟后执行
		),
		sched.NewJob(func() {
			fmt.Println("特定时间任务执行")
		}),
	)

	// 复杂的重复规则: 每1小时执行,但只在工作时间内(9:00-17:00)
	s.Schedule(
		sched.Every(sched.Hours(1)).While(
			sched.NewTimeConstraint(
				sched.Hour(9), // 开始时间: 9:00
				sched.Hour(17), // 结束时间: 17:00
			),
		),
		sched.NewJob(func() {
			fmt.Println("工作时间内的每小时任务执行")
		}),
	)

	// 快进2天查看任务执行情况
	s.FastForward(sched.Days(2))

	s.Close()
}

sched库提供了灵活的调度选项和强大的时间快进功能,非常适合需要测试定时任务行为的应用场景。


更多关于golang实现时间快进功能的作业调度插件库sched的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现时间快进功能的作业调度插件库sched的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 实现时间快进功能的作业调度插件库 sched

在 Golang 中实现一个具有时间快进功能的作业调度库可以非常有用,特别是在测试环境中,我们可以模拟时间的快速流逝来验证定时任务的执行情况。下面我将介绍如何使用和实现这样一个库。

基本概念

时间快进功能指的是我们可以控制时间的流动速度,比如让系统认为时间已经过去了1小时,而实际上只过了1秒钟。这对于测试定时任务、批处理作业等场景非常有用。

实现方案

我们可以创建一个虚拟时钟接口,并实现真实时钟和模拟时钟两种版本。在测试时使用模拟时钟,可以自由控制时间的流逝。

1. 定义时钟接口

package sched

import "time"

// Clock 定义时钟接口
type Clock interface {
    Now() time.Time
    Sleep(d time.Duration)
    After(d time.Duration) <-chan time.Time
    AfterFunc(d time.Duration, f func()) *Timer
    Since(t time.Time) time.Duration
    NewTicker(d time.Duration) *Ticker
}

// Timer 模拟time.Timer
type Timer struct {
    C <-chan time.Time
    // 其他实现细节...
}

// Ticker 模拟time.Ticker
type Ticker struct {
    C <-chan time.Time
    // 其他实现细节...
}

2. 实现真实时钟

// RealClock 真实时钟实现
type RealClock struct{}

func (RealClock) Now() time.Time {
    return time.Now()
}

func (RealClock) Sleep(d time.Duration) {
    time.Sleep(d)
}

func (RealClock) After(d time.Duration) <-chan time.Time {
    return time.After(d)
}

func (RealClock) AfterFunc(d time.Duration, f func()) *Timer {
    return &Timer{C: time.AfterFunc(d, f).C}
}

func (RealClock) Since(t time.Time) time.Duration {
    return time.Since(t)
}

func (RealClock) NewTicker(d time.Duration) *Ticker {
    return &Ticker{C: time.NewTicker(d).C}
}

3. 实现模拟时钟

// MockClock 模拟时钟实现
type MockClock struct {
    now time.Time
    mu  sync.Mutex
}

func NewMockClock() *MockClock {
    return &MockClock{now: time.Now()}
}

func (m *MockClock) Now() time.Time {
    m.mu.Lock()
    defer m.mu.Unlock()
    return m.now
}

func (m *MockClock) Add(d time.Duration) {
    m.mu.Lock()
    m.now = m.now.Add(d)
    m.mu.Unlock()
}

func (m *MockClock) Sleep(d time.Duration) {
    m.Add(d)
}

// 其他方法实现类似...

4. 创建调度器

type Scheduler struct {
    clock Clock
    jobs  map[string]*Job
    mu    sync.Mutex
}

type Job struct {
    ID       string
    Schedule time.Duration
    Task     func()
    stop     chan struct{}
}

func NewScheduler(clock Clock) *Scheduler {
    return &Scheduler{
        clock: clock,
        jobs:  make(map[string]*Job),
    }
}

func (s *Scheduler) AddJob(id string, interval time.Duration, task func()) {
    s.mu.Lock()
    defer s.mu.Unlock()
    
    if _, exists := s.jobs[id]; exists {
        return
    }
    
    job := &Job{
        ID:       id,
        Schedule: interval,
        Task:     task,
        stop:     make(chan struct{}),
    }
    
    s.jobs[id] = job
    go s.runJob(job)
}

func (s *Scheduler) runJob(job *Job) {
    ticker := s.clock.NewTicker(job.Schedule)
    defer ticker.Stop()
    
    for {
        select {
        case <-ticker.C:
            job.Task()
        case <-job.stop:
            return
        }
    }
}

func (s *Scheduler) RemoveJob(id string) {
    s.mu.Lock()
    defer s.mu.Unlock()
    
    if job, exists := s.jobs[id]; exists {
        close(job.stop)
        delete(s.jobs, id)
    }
}

5. 使用示例

func main() {
    // 生产环境使用真实时钟
    realScheduler := sched.NewScheduler(&sched.RealClock{})
    realScheduler.AddJob("heartbeat", 5*time.Second, func() {
        fmt.Println("Heartbeat at", time.Now())
    })
    
    // 测试环境使用模拟时钟
    mockClock := sched.NewMockClock()
    testScheduler := sched.NewScheduler(mockClock)
    
    var count int
    testScheduler.AddJob("test", time.Minute, func() {
        count++
        fmt.Printf("Test job executed %d times\n", count)
    })
    
    // 快进时间
    fmt.Println("Start time:", mockClock.Now())
    mockClock.Add(30 * time.Second)
    fmt.Println("After 30s:", mockClock.Now())
    mockClock.Add(30 * time.Second) // 总共1分钟过去,应该触发一次
    fmt.Println("After 1m:", mockClock.Now())
    
    time.Sleep(2 * time.Second) // 等待goroutine执行
    fmt.Println("Final count:", count) // 应该输出1
}

高级功能扩展

  1. CRON表达式支持:可以添加对CRON表达式的解析支持
  2. 持久化存储:将作业配置保存到数据库
  3. 分布式锁:在分布式环境中防止重复执行
  4. 作业重试机制:添加失败重试逻辑
  5. 作业依赖:实现作业之间的依赖关系

测试优势

使用这种模式进行测试有几个显著优势:

  1. 测试速度快:不需要真实等待时间流逝
  2. 确定性:可以精确控制时间点,测试边界条件
  3. 可重复性:测试不依赖于真实时间,结果可重复
  4. 全面覆盖:可以测试很难模拟的真实场景(如闰秒、时区变更等)

这个实现提供了一个基础框架,你可以根据实际需求进行扩展和完善。

回到顶部