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  }