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  }