gitee.com/h79/goutils@v1.22.10/discovery/health/health.go (about) 1 package health 2 3 import ( 4 "context" 5 "fmt" 6 "gitee.com/h79/goutils/alarm" 7 commonhttp "gitee.com/h79/goutils/common/http" 8 "gitee.com/h79/goutils/common/logger" 9 "gitee.com/h79/goutils/common/result" 10 "gitee.com/h79/goutils/common/system" 11 "gitee.com/h79/goutils/discovery/service" 12 "net/http" 13 "strings" 14 "sync" 15 "time" 16 ) 17 18 var _ service.Base = (*Health)(nil) 19 20 type Health struct { 21 conf Config 22 locker sync.RWMutex 23 nodes map[string]*Node 24 stop chan bool 25 } 26 27 func New(conf Config) *Health { 28 if conf.MaxCount <= 0 { 29 conf.MaxCount = 3 30 } 31 if conf.Interval <= 0 { 32 conf.Interval = 30 33 } 34 return &Health{ 35 conf: conf, 36 stop: make(chan bool), 37 nodes: make(map[string]*Node), 38 } 39 } 40 41 func (ch *Health) AddNode(id string, node *Node) { 42 ch.locker.Lock() 43 defer ch.locker.Unlock() 44 if _, ok := ch.nodes[id]; ok { 45 return 46 } 47 ch.nodes[id] = node 48 } 49 50 func (ch *Health) DelNode(id string) { 51 ch.locker.Lock() 52 defer ch.locker.Unlock() 53 delete(ch.nodes, id) 54 } 55 56 func (ch *Health) Start() error { 57 system.ChildRunning(ch.check) 58 return nil 59 } 60 61 func (ch *Health) Stop() { 62 system.Stop(time.Second, ch.stop) 63 } 64 65 func (ch *Health) check() { 66 ticker := time.NewTicker(time.Second * ch.conf.Interval) 67 defer ticker.Stop() 68 for { 69 select { 70 case <-ticker.C: 71 ch.run() 72 73 case <-ch.stop: 74 ch.stop <- true 75 return 76 77 case <-system.Closed(): 78 return 79 } 80 } 81 } 82 83 func (ch *Health) run() { 84 nodes := make(map[string]*Node) 85 ch.locker.RLock() 86 for id, node := range ch.nodes { 87 nodes[id] = node 88 } 89 ch.locker.RUnlock() 90 91 for _, node := range nodes { 92 if err := ch.checkNode(node); err != nil { 93 node.errCount++ 94 logger.Error("Health: check failure, err= '%v'", err) 95 } else { 96 node.errCount = 0 97 } 98 if node.errCount > ch.conf.MaxCount { 99 logger.Error("Health: check failure, err count > max count(%v)", node.errCount, ch.conf.MaxCount) 100 //报警 101 ch.alarm(node) 102 } 103 } 104 } 105 106 func (ch *Health) alarm(node *Node) { 107 if !ch.conf.EnableAlarm { 108 return 109 } 110 alarm.Important(context.Background(), result.ErrTimeout, "discovery", "", fmt.Sprintf("Health: node health check error, node= %+v", node), nil) 111 } 112 113 func (ch *Health) checkNode(node *Node) error { 114 if node.Check != nil { 115 return node.Check(node) 116 } 117 if strings.Contains(node.Health.Protocol, "http") { 118 return ch.http(node) 119 } 120 return result.RErrNotSupport 121 } 122 123 func (ch *Health) http(node *Node) error { 124 hp := commonhttp.Http{} 125 body, err := hp.Do(node.Health.Method, node.URL(), nil, func(h *http.Header) { 126 }) 127 if err != nil { 128 return err 129 } 130 if node.Unpack != nil { 131 return node.Unpack(body, node) 132 } 133 return nil 134 }