第一版
This commit is contained in:
112
services/timer.go
Normal file
112
services/timer.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"log"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"go-taobao/config"
|
||||
"go-taobao/utils"
|
||||
)
|
||||
|
||||
// Timer 定时触发器,精确控制抢购时机
|
||||
type Timer struct {
|
||||
cfg *config.RushConfig
|
||||
timeSync *utils.TimeSync
|
||||
}
|
||||
|
||||
// NewTimer 创建定时器
|
||||
func NewTimer(cfg *config.RushConfig, ts *utils.TimeSync) *Timer {
|
||||
return &Timer{cfg: cfg, timeSync: ts}
|
||||
}
|
||||
|
||||
// WaitUntilPreload 等待到预加载时间点(目标时间 - PreloadBefore)
|
||||
// 返回后应立即开始预加载购物车
|
||||
func (t *Timer) WaitUntilPreload() {
|
||||
preloadTime := t.cfg.TargetTime.Add(-t.cfg.PreloadBefore)
|
||||
remaining := t.timeSync.UntilTarget(preloadTime)
|
||||
|
||||
if remaining <= 0 {
|
||||
log.Println("[定时器] 已过预加载时间,立即执行")
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[定时器] 距离预加载还有 %v,等待中...", remaining.Round(time.Second))
|
||||
t.sleepUntil(preloadTime)
|
||||
log.Println("[定时器] 预加载时间到!")
|
||||
}
|
||||
|
||||
// WaitUntilRush 等待到抢购时间点(目标时间 - SubmitAdvance)
|
||||
// 使用 Sleep + 自旋忙等 的混合策略确保毫秒级精度
|
||||
func (t *Timer) WaitUntilRush() {
|
||||
// 实际触发时间 = 目标时间 - 提前量(补偿网络延迟)
|
||||
rushTime := t.cfg.TargetTime.Add(-t.cfg.SubmitAdvance)
|
||||
remaining := t.timeSync.UntilTarget(rushTime)
|
||||
|
||||
if remaining <= 0 {
|
||||
log.Println("[定时器] 已过抢购时间,立即执行")
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[定时器] 距离抢购还有 %v (含 %v 提前量)",
|
||||
remaining.Round(time.Millisecond), t.cfg.SubmitAdvance)
|
||||
|
||||
t.sleepUntil(rushTime)
|
||||
|
||||
serverNow := t.timeSync.Now()
|
||||
diff := serverNow.Sub(t.cfg.TargetTime)
|
||||
log.Printf("[定时器] 触发! 服务器时间: %s (偏差: %v)",
|
||||
serverNow.Format("15:04:05.000"), diff)
|
||||
}
|
||||
|
||||
// sleepUntil 混合等待策略:先 Sleep 粗等,最后 3 秒自旋忙等
|
||||
func (t *Timer) sleepUntil(target time.Time) {
|
||||
// 阶段1: 距离目标 >5s 时,用 Sleep 粗等(省 CPU)
|
||||
for {
|
||||
remaining := t.timeSync.UntilTarget(target)
|
||||
if remaining <= 3*time.Second {
|
||||
break
|
||||
}
|
||||
// 每次 sleep 剩余时间的一半,逐步逼近
|
||||
sleepDur := remaining / 2
|
||||
if sleepDur > 1*time.Second {
|
||||
sleepDur = 1 * time.Second
|
||||
}
|
||||
time.Sleep(sleepDur)
|
||||
}
|
||||
|
||||
// 阶段2: 最后 3 秒,锁定 OS 线程 + 自旋忙等(最高精度)
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
log.Println("[定时器] 进入自旋忙等模式...")
|
||||
for t.timeSync.Now().Before(target) {
|
||||
// 自旋,不让出 CPU
|
||||
runtime.Gosched() // 偶尔让出一下,避免完全饿死其他 goroutine
|
||||
}
|
||||
}
|
||||
|
||||
// PrintCountdown 持续打印倒计时日志(在单独的 goroutine 中运行)
|
||||
func (t *Timer) PrintCountdown(done <-chan struct{}) {
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-ticker.C:
|
||||
remaining := t.timeSync.UntilTarget(t.cfg.TargetTime)
|
||||
if remaining <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
hours := int(remaining.Hours())
|
||||
mins := int(remaining.Minutes()) % 60
|
||||
secs := int(remaining.Seconds()) % 60
|
||||
log.Printf("[倒计时] %02d:%02d:%02d (服务器时间: %s)",
|
||||
hours, mins, secs,
|
||||
t.timeSync.Now().Format("15:04:05"))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user