You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
3.2 KiB
Go
146 lines
3.2 KiB
Go
package health
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/zeromicro/go-zero/core/syncx"
|
|
)
|
|
|
|
// defaultHealthManager is global comboHealthManager for byone self.
|
|
var defaultHealthManager = newComboHealthManager()
|
|
|
|
type (
|
|
// Probe represents readiness status of given component.
|
|
Probe interface {
|
|
// MarkReady sets a ready state for the endpoint handlers.
|
|
MarkReady()
|
|
// MarkNotReady sets a not ready state for the endpoint handlers.
|
|
MarkNotReady()
|
|
// IsReady return inner state for the component.
|
|
IsReady() bool
|
|
// Name return probe name identifier
|
|
Name() string
|
|
}
|
|
|
|
// healthManager manage app healthy.
|
|
healthManager struct {
|
|
ready syncx.AtomicBool
|
|
name string
|
|
}
|
|
|
|
// comboHealthManager folds given probes into one, reflects their statuses in a thread-safe way.
|
|
comboHealthManager struct {
|
|
mu sync.Mutex
|
|
probes []Probe
|
|
}
|
|
)
|
|
|
|
// AddProbe add components probe to global comboHealthManager.
|
|
func AddProbe(probe Probe) {
|
|
defaultHealthManager.addProbe(probe)
|
|
}
|
|
|
|
// CreateHttpHandler create health http handler base on given probe.
|
|
func CreateHttpHandler() http.HandlerFunc {
|
|
return func(w http.ResponseWriter, _ *http.Request) {
|
|
if defaultHealthManager.IsReady() {
|
|
_, _ = w.Write([]byte("OK"))
|
|
} else {
|
|
http.Error(w, "Service Unavailable\n"+defaultHealthManager.verboseInfo(),
|
|
http.StatusServiceUnavailable)
|
|
}
|
|
}
|
|
}
|
|
|
|
// NewHealthManager returns a new healthManager.
|
|
func NewHealthManager(name string) Probe {
|
|
return &healthManager{
|
|
name: name,
|
|
}
|
|
}
|
|
|
|
// MarkReady sets a ready state for the endpoint handlers.
|
|
func (h *healthManager) MarkReady() {
|
|
h.ready.Set(true)
|
|
}
|
|
|
|
// MarkNotReady sets a not ready state for the endpoint handlers.
|
|
func (h *healthManager) MarkNotReady() {
|
|
h.ready.Set(false)
|
|
}
|
|
|
|
// IsReady return inner state for the component.
|
|
func (h *healthManager) IsReady() bool {
|
|
return h.ready.True()
|
|
}
|
|
|
|
// Name return probe name identifier
|
|
func (h *healthManager) Name() string {
|
|
return h.name
|
|
}
|
|
|
|
func newComboHealthManager() *comboHealthManager {
|
|
return &comboHealthManager{}
|
|
}
|
|
|
|
// MarkReady sets components status to ready.
|
|
func (p *comboHealthManager) MarkReady() {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
|
|
for _, probe := range p.probes {
|
|
probe.MarkReady()
|
|
}
|
|
}
|
|
|
|
// MarkNotReady sets components status to not ready with given error as a cause.
|
|
func (p *comboHealthManager) MarkNotReady() {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
|
|
for _, probe := range p.probes {
|
|
probe.MarkNotReady()
|
|
}
|
|
}
|
|
|
|
// IsReady return composed status of all components.
|
|
func (p *comboHealthManager) IsReady() bool {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
|
|
for _, probe := range p.probes {
|
|
if !probe.IsReady() {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (p *comboHealthManager) verboseInfo() string {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
|
|
var info strings.Builder
|
|
for _, probe := range p.probes {
|
|
if probe.IsReady() {
|
|
info.WriteString(fmt.Sprintf("%s is ready\n", probe.Name()))
|
|
} else {
|
|
info.WriteString(fmt.Sprintf("%s is not ready\n", probe.Name()))
|
|
}
|
|
}
|
|
|
|
return info.String()
|
|
}
|
|
|
|
// addProbe add components probe to comboHealthManager.
|
|
func (p *comboHealthManager) addProbe(probe Probe) {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
|
|
p.probes = append(p.probes, probe)
|
|
}
|