0%

并行访问最快返回

设想这样一个场景:服务器需要同步时间服务,希望访问若干个时间同步服务,取最快且正确返回结果的服务作为结果。通过Go并行访问这若干个服务器,只取最快返回且正确的,其它结果统统丢弃。下面是这样的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
var (
ntpServers = []string{
"us.pool.ntp.org:123",
"time.cloudflare.com:123",
"time.windows.com:123",
"time.apple.com:123",
"time.google.com:123",
"time.nist.gov:123",
"ntp.ntsc.ac.cn:123",
"time.smg.gov.mo:123",
"stdtime.gov.hk:123",
}
)

func NewGroupVisitor[T any]() *GroupVisitor[T] {
return &GroupVisitor[T]{result: make(chan T)}
}

type GroupVisitor[T any] struct {
wg sync.WaitGroup
doneOnce sync.Once
result chan T
}

func (g *GroupVisitor[T]) Go(f func() (T, string, bool, error)) {
g.wg.Add(1)
go func() {
defer g.wg.Done()
if data, host, done, err := f(); done && err == nil {
g.doneOnce.Do(func() {
g.result <- data
})
}
}()
}

func (g *GroupVisitor[T]) Wait(duration time.Duration) (T, error) {
var empty T
select {
case <-time.After(duration):
return empty, errors.New("request timeout")
case ret := <-g.result:
return ret, nil
}
}

func main() {
g := NewGroupVisitor[*time.Time]()
for _, host := range ntpServers {
host := host
g.Go(func() (*time.Time, string, bool, error) {
respTime, err := ntp.Time(host)
if err != nil {
return nil, host, false, err
}

return &respTime, host, true, nil
})
}

clock, err := g.Wait(6 * time.Second)
if err != nil {
return
}
}