如何简单实现⼀个定时器程序,能够实现在指定时间执⾏指定程序,如果程序重启怎么办?
实现方案
可以使用Go语言内置的time
和os/exec
包来实现定时器程序。
实现分析
- 解析传入的程序二进制文件路径和计划时间。
- 计算当前时间和计划时间的时间差,使用time包中的定时器函数time.After()来设置定时器,等待时间差到达后触发定时器事件。
- 在定时器事件中,使用os/exec包中的函数来执行指定的程序,若启动失败可以设置重新启动。
对于程序重启,对本程序功能并不影响:
- 本定时器重启的场景:定时任务时间等待时间差是在程序启动时实时计算的,若重启了还是会重新计算当前时间差再次计时;
- 执行目标程序重启,当前计时器程序目的就是去执行目标程序,目标程序重启不影响本程序功能。
实现步骤
准备一个 “可执行程序”
package main
import "fmt"
func main() {
fmt.Println("测试拉起二进制成功!!!!")
}
编译出可执行程序:(假设路径是"D:/test/testCall.exe")
go build .\testCall.go
实现定时器程序
package main
import (
"fmt"
"os"
"os/exec"
"time"
)
const (
argNum = 3
retryCount = 3
)
func main() {
// 解析传入的程序二进制文件路径和计划时间
if len(os.Args) < argNum {
fmt.Println("Usage: go run main.go [binary_path] [plan_time]")
os.Exit(1)
}
binaryPath := os.Args[1]
loc, _ := time.LoadLocation("Asia/Shanghai")
planTime, err := time.ParseInLocation("2006-01-02 15:04:05", os.Args[2], loc)
if err != nil {
fmt.Println("Error: Invalid plan time format,", err)
os.Exit(1)
}
// 启动程序时记录启动时间
now := time.Now().Local().UTC()
fmt.Println("Info: now = ", now)
fmt.Println("Info: planTime = ", planTime)
// 计算时间差并设置定时器
duration := planTime.Sub(now)
fmt.Println("Info: duration =", duration)
if duration > 0 { // 是否需要定时器,防止是定时器程序重启过
timer := time.NewTimer(duration)
fmt.Println("Info: waiting timer")
// 等待定时器事件
<-timer.C
}
// 执行指定程序
for i := 0; i < retryCount; i++ { // 最多尝试执行3次
cmd := exec.Command(binaryPath)
output, err := cmd.Output()
if err == nil {
fmt.Println(string(output))
fmt.Println("Even: binary executed successfully")
return
}
fmt.Println("Warn: Failed to execute program:", err)
time.Sleep(5 * time.Second) // 等待5秒再尝试执行
}
fmt.Println("Error: binary execution failed")
}
执行程序:
go run main.go "D:/test/testCall.exe" "2024-08-26 19:48:05"
或者 go build 编译出来后执行:
.\main.exe "D:/test/testCall.exe" "2024-08-26 19:48:05"
测试结果
Info: now = 2024-08-26 20:42:55.4405164 +0800 CST
Info: planTime = 2024-08-26 19:48:05 +0800 CST
Info: duration = -54m50.4405164s
测试拉起二进制成功!!!!
Even: binary executed successfully