113 lines
3.1 KiB
Go
113 lines
3.1 KiB
Go
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"))
|
||
}
|
||
}
|
||
}
|