health.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package health
  2. import (
  3. "fmt"
  4. "net/http"
  5. "strings"
  6. "sync"
  7. "github.com/wuntsong-org/go-zero-plus/core/syncx"
  8. )
  9. // defaultHealthManager is global comboHealthManager.
  10. var defaultHealthManager = newComboHealthManager()
  11. type (
  12. // Probe represents readiness status of given component.
  13. Probe interface {
  14. // MarkReady sets a ready state for the endpoint handlers.
  15. MarkReady()
  16. // MarkNotReady sets a not ready state for the endpoint handlers.
  17. MarkNotReady()
  18. // IsReady return inner state for the component.
  19. IsReady() bool
  20. // Name return probe name identifier
  21. Name() string
  22. }
  23. // healthManager manage app healthy.
  24. healthManager struct {
  25. ready syncx.AtomicBool
  26. name string
  27. }
  28. // comboHealthManager folds given probes into one, reflects their statuses in a thread-safe way.
  29. comboHealthManager struct {
  30. mu sync.Mutex
  31. probes []Probe
  32. }
  33. )
  34. // AddProbe add components probe to global comboHealthManager.
  35. func AddProbe(probe Probe) {
  36. defaultHealthManager.addProbe(probe)
  37. }
  38. // CreateHttpHandler create health http handler base on given probe.
  39. func CreateHttpHandler() http.HandlerFunc {
  40. return func(w http.ResponseWriter, _ *http.Request) {
  41. if defaultHealthManager.IsReady() {
  42. _, _ = w.Write([]byte("OK"))
  43. } else {
  44. http.Error(w, "Service Unavailable\n"+defaultHealthManager.verboseInfo(),
  45. http.StatusServiceUnavailable)
  46. }
  47. }
  48. }
  49. // NewHealthManager returns a new healthManager.
  50. func NewHealthManager(name string) Probe {
  51. return &healthManager{
  52. name: name,
  53. }
  54. }
  55. // MarkReady sets a ready state for the endpoint handlers.
  56. func (h *healthManager) MarkReady() {
  57. h.ready.Set(true)
  58. }
  59. // MarkNotReady sets a not ready state for the endpoint handlers.
  60. func (h *healthManager) MarkNotReady() {
  61. h.ready.Set(false)
  62. }
  63. // IsReady return inner state for the component.
  64. func (h *healthManager) IsReady() bool {
  65. return h.ready.True()
  66. }
  67. // Name return probe name identifier
  68. func (h *healthManager) Name() string {
  69. return h.name
  70. }
  71. func newComboHealthManager() *comboHealthManager {
  72. return &comboHealthManager{}
  73. }
  74. // MarkReady sets components status to ready.
  75. func (p *comboHealthManager) MarkReady() {
  76. p.mu.Lock()
  77. defer p.mu.Unlock()
  78. for _, probe := range p.probes {
  79. probe.MarkReady()
  80. }
  81. }
  82. // MarkNotReady sets components status to not ready with given error as a cause.
  83. func (p *comboHealthManager) MarkNotReady() {
  84. p.mu.Lock()
  85. defer p.mu.Unlock()
  86. for _, probe := range p.probes {
  87. probe.MarkNotReady()
  88. }
  89. }
  90. // IsReady return composed status of all components.
  91. func (p *comboHealthManager) IsReady() bool {
  92. p.mu.Lock()
  93. defer p.mu.Unlock()
  94. for _, probe := range p.probes {
  95. if !probe.IsReady() {
  96. return false
  97. }
  98. }
  99. return true
  100. }
  101. func (p *comboHealthManager) verboseInfo() string {
  102. p.mu.Lock()
  103. defer p.mu.Unlock()
  104. var info strings.Builder
  105. for _, probe := range p.probes {
  106. if probe.IsReady() {
  107. info.WriteString(fmt.Sprintf("%s is ready\n", probe.Name()))
  108. } else {
  109. info.WriteString(fmt.Sprintf("%s is not ready\n", probe.Name()))
  110. }
  111. }
  112. return info.String()
  113. }
  114. // addProbe add components probe to comboHealthManager.
  115. func (p *comboHealthManager) addProbe(probe Probe) {
  116. p.mu.Lock()
  117. defer p.mu.Unlock()
  118. p.probes = append(p.probes, probe)
  119. }