ntp.go 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. package netmgrd
  2. import (
  3. "fmt"
  4. "math"
  5. "os/exec"
  6. "sync"
  7. "time"
  8. "github.com/beevik/ntp"
  9. )
  10. var servers = []string{
  11. "210.72.145.44",
  12. "ntp.aliyun.com",
  13. "ntp.ntsc.ac.cn",
  14. }
  15. type ntpResult struct {
  16. t time.Time
  17. d time.Duration
  18. err error
  19. }
  20. func SyncNTPTime() error {
  21. t, err := getAccurateTime(servers, 5*time.Second)
  22. if err != nil {
  23. return err
  24. }
  25. if err := SetSystemTime(t); err != nil {
  26. return err
  27. }
  28. return nil
  29. }
  30. func getAccurateTime(servers []string, timeout time.Duration) (time.Time, error) {
  31. ch := make(chan ntpResult, len(servers))
  32. var wg sync.WaitGroup
  33. wg.Add(len(servers))
  34. for _, s := range servers {
  35. go func(s string) {
  36. defer wg.Done()
  37. resp, err := ntp.QueryWithOptions(s, ntp.QueryOptions{Timeout: timeout})
  38. if err != nil {
  39. ch <- ntpResult{err: err}
  40. return
  41. }
  42. if resp.Stratum == 0 || resp.Time.IsZero() {
  43. ch <- ntpResult{err: fmt.Errorf("invalid ntp response")}
  44. return
  45. }
  46. t := resp.Time.Add(resp.RTT / 2)
  47. d := resp.RootDistance // 最大可能偏差
  48. ch <- ntpResult{t: t, d: d, err: nil}
  49. }(s)
  50. }
  51. go func() {
  52. wg.Wait()
  53. close(ch)
  54. }()
  55. var bestTime time.Time
  56. var bestDist time.Duration = math.MaxInt64
  57. for r := range ch {
  58. if r.err != nil {
  59. continue
  60. }
  61. if r.d < bestDist {
  62. bestDist = r.d
  63. bestTime = r.t
  64. }
  65. }
  66. if bestTime.IsZero() {
  67. return time.Time{}, fmt.Errorf("no valid ntp response")
  68. }
  69. return bestTime, nil
  70. }
  71. var setTimeMu sync.Mutex // 保证调用 SetSystemTime() 函数的线程安全
  72. func SetSystemTime(t time.Time) error {
  73. setTimeMu.Lock()
  74. defer setTimeMu.Unlock()
  75. cmd := exec.Command("date", "-s", t.Local().Format("2006-01-02 15:04:05"))
  76. output, err := cmd.CombinedOutput()
  77. if err != nil {
  78. return fmt.Errorf("%v, output: %s", err, string(output))
  79. }
  80. return nil
  81. }