github.com/sandwich-go/boost@v1.3.29/xtime/cop.go (about) 1 package xtime 2 3 import ( 4 "fmt" 5 "github.com/sandwich-go/boost/internal/log" 6 "github.com/sandwich-go/boost/xpanic" 7 "math" 8 "time" 9 10 "github.com/sandwich-go/boost/xsync" 11 ) 12 13 var ( 14 // CopToleranceSecond 时间容忍误差,误差超过CopToleranceSecond则后续使用系统时间 15 CopToleranceSecond = int64(2) 16 // CopToleranceCheckInterval 检测CopToleranceSecond的时间间隔 17 CopToleranceCheckInterval = time.Duration(10) * time.Second 18 ) 19 20 // Cop mock time like ruby's time cop 21 type Cop struct { 22 ts xsync.AtomicInt64 23 unhealthy chan struct{} 24 closeChan chan struct{} 25 running xsync.AtomicBool 26 closeFlag xsync.AtomicInt32 27 nowProvider func() time.Time 28 } 29 30 // NewCop 新建Cop对象 31 func NewCop(nowProvider func() time.Time) *Cop { 32 tc := &Cop{nowProvider: nowProvider} 33 tc.closeChan = make(chan struct{}) 34 tc.unhealthy = make(chan struct{}) 35 tc.run() 36 return tc 37 } 38 39 func (tc *Cop) run() { 40 go xpanic.AutoRecover("stime_cop", tc.start) 41 go xpanic.AutoRecover("stime_cop_check", tc.check) 42 } 43 44 func (tc *Cop) check() { 45 checkTicker := time.NewTicker(CopToleranceCheckInterval) 46 defer checkTicker.Stop() 47 for { 48 select { 49 case <-checkTicker.C: 50 systemTime := tc.nowProvider().Unix() 51 copTime := tc.Unix() 52 if int64(math.Abs(float64(systemTime-copTime))) > CopToleranceSecond { 53 log.Warn(fmt.Sprintf("stime cop tolerance, system: %d, cop: %d, tolerance: %d", systemTime, copTime, CopToleranceSecond)) 54 tc.unhealthy <- struct{}{} 55 } 56 return 57 case <-tc.closeChan: 58 return 59 } 60 } 61 } 62 63 func (tc *Cop) start() { 64 tc.running.Set(true) 65 defer func() { 66 tc.running.Set(false) 67 }() 68 tc.ts.Set(tc.nowProvider().Unix()) 69 ticker := time.NewTicker(1 * time.Second) 70 defer ticker.Stop() 71 for { 72 select { 73 case now := <-ticker.C: 74 tc.ts.Set(now.Unix()) 75 case <-tc.closeChan: 76 return 77 case <-tc.unhealthy: 78 // 处于不健康状态,暂停使用 79 return 80 } 81 } 82 } 83 84 // Stop 停止模拟时间 85 func (tc *Cop) Stop() { 86 if tc.closeFlag.CompareAndSwap(0, 1) { 87 close(tc.closeChan) 88 tc.running.Set(false) 89 } 90 } 91 92 // Now Cop获取的最小单位为秒,精度低但是效率高, time.Now()最小单位为纳秒 93 func (tc *Cop) Now() time.Time { 94 if tc.running.Get() { 95 return time.Unix(tc.ts.Get(), 0) 96 } 97 return tc.nowProvider() 98 } 99 100 // Unix 获取当前Unix时间戳 101 func (tc *Cop) Unix() int64 { return tc.Now().Unix() } 102 103 // UnixMilli 获取当前时间,单位毫秒 104 func (tc *Cop) UnixMilli() int64 { 105 return tc.Now().UnixNano() / int64(time.Millisecond) 106 }