Goroutine心跳檢測
心跳檢測的核心思想是,單獨啓動心跳協程,然後通過向協程發送數據,表示心跳。
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
doWork := func(done <-chan interface{},
pulseInterval time.Duration,
genData func() interface{},
) (<-chan interface{}, <-chan interface{}) {
heartBeat := make(chan interface{}) // 心跳
results := make(chan interface{})
go func() {
defer close(heartBeat)
defer close(results)
sendPulse := func() {
select {
case heartBeat <- struct{}{}:
default: // 心跳不一定有人接收
}
}
pulse := time.Tick(pulseInterval)
sendResult := func(r interface{}) {
for {
select {
case <-done:
return
case <-pulse: // 接收channel可能阻塞,因此持續心跳
sendPulse()
case results <- r:
return // 耗時任務完成後,立刻返回
}
}
}
bridge := make(chan interface{})
go func() {
defer close(res)
bridge <- genData() // 我們假設會一直等到有數據發送過來
}()
for {
select {
case <-done:
return
case <-pulse:
sendPulse()
case r, ok := <-res:
if !ok { // 前邊有關掉res了,所以這裏需要ok判斷
return
}
sendResult(r)
}
}
}()
return heartBeat, results
}
genMyData := func() interface{} {
t := rand.Intn(10) + 3
time.Sleep(time.Second * time.Duration(t))
fmt.Printf("genMyData weak up after:%v\n", t)
return "foo"
}
done := make(chan interface{})
defer close(done)
interval := time.Second
heatBeat, result := doWork(done,interval, genMyData)
for {
select {
case _, ok := <-heatBeat:
if !ok {
return
}
fmt.Println("hearbeat")
case r, ok := <-result:
if !ok {
return
}
fmt.Printf("get result: %v\n", r)
case <-time.After(interval * 2): // 2倍心跳時間找不到心跳信號,則認爲超時
fmt.Printf("timeout")
return
}
}
}