golang异步服务健康检查库go-sundheit的使用

go-sundheit 异步服务健康检查库的使用

go-sundheit 是一个用于定义 Golang 服务健康检查的库。它允许你为依赖项和服务本身注册异步健康检查,并提供一个暴露它们状态的健康检查端点。

什么是 go-sundheit?

项目名称来源于德语单词 “Gesundheit”,意思是"健康",发音为 /ɡəˈzʊntˌhaɪ̯t/。

安装

使用 go modules:

go get github.com/AppsFlyer/go-sundheit@v0.5.0

使用示例

下面是一个完整的使用示例:

import (
	"net/http"
	"time"
	"log"

	"github.com/pkg/errors"
	"github.com/AppsFlyer/go-sundheit"

	healthhttp "github.com/AppsFlyer/go-sundheit/http"
	"github.com/AppsFlyer/go-sundheit/checks"
)

func main() {
	// 创建一个新的健康检查实例
	h := gosundheit.New()
	
	// 定义一个HTTP依赖检查
	httpCheckConf := checks.HTTPCheckConfig{
		CheckName: "httpbin.url.check",
		Timeout:   1 * time.Second,
		// 这里检查你的依赖 - 使用你自己的URL...
		// 这个URL会有50%的失败率
		URL:       "http://httpbin.org/status/200,300",
	}
	// 为依赖创建HTTP检查
	// 当URL配置错误时快速失败,不要忽略错误!!!
	httpCheck, err := checks.NewHTTPCheck(httpCheckConf)
	if err != nil {
		fmt.Println(err)
		return // 根据你的需求处理...
	}

	// 或者创建检查失败时直接panic
	httpCheck = checks.Must(checks.NewHTTPCheck(httpCheckConf))

	err = h.RegisterCheck(
		httpCheck,
		gosundheit.InitialDelay(time.Second),         // 检查将在1秒后首次运行
		gosundheit.ExecutionPeriod(10 * time.Second), // 检查将每10秒执行一次
	)
	
	if err != nil {
		fmt.Println("Failed to register check: ", err)
		return // 或其他处理
	}

	// 定义更多检查...
	
	// 注册健康检查端点
	http.Handle("/admin/health.json", healthhttp.HandleHealthJSON(h))
	
	// 启动HTTP服务
	log.Fatal(http.ListenAndServe(":8080", nil))
}

内置检查

库提供了一组内置检查:

HTTP 检查

HTTP 检查允许你触发对依赖项的 HTTP 请求,并验证响应状态和可选地验证响应体内容。

DNS 检查

DNS 检查允许你对给定的主机名/域名/CNAME 等执行查找,并验证它是否解析为至少所需数量的结果。

// 为example.com安排一个主机解析检查,要求至少一个结果,每10秒运行一次
h.RegisterCheck(
	checks.NewHostResolveCheck("example.com", 1),
	gosundheit.ExecutionPeriod(10 * time.Second),
)

Ping 检查

Ping 检查允许你验证资源是否仍然存活和可访问。例如,你可以将其用作数据库 ping 检查:

db, err := sql.Open(...)
dbCheck, err := checks.NewPingCheck("db.check", db)
_ = h.RegisterCheck(&gosundheit.Config{
	Check: dbCheck,
	// ...
})

自定义检查

库提供了两种定义自定义检查的方式:

使用 CustomCheck 结构体

func lotteryCheck() (details interface{}, err error) {
	lottery := rand.Float32()
	details = fmt.Sprintf("lottery=%f", lottery)
	if lottery < 0.5 {
		err = errors.New("Sorry, I failed")
	}
	return
}

h.RegisterCheck(
	&checks.CustomCheck{
		CheckName: "lottery.check",
		CheckFunc: lotteryCheck,
	},
	gosundheit.InitialDelay(0),
	gosundheit.ExecutionPeriod(2 * time.Minute), 
	gosundheit.ExecutionTimeout(5 * time.Second)
)

实现 Check 接口

type Lottery struct {
	myname string
	probability float32
}

func (l Lottery) Execute() (details interface{}, err error) {
	lottery := rand.Float32()
	details = fmt.Sprintf("lottery=%f", lottery)
	if lottery < l.probability {
		err = errors.New("Sorry, I failed")
	}
	return
}

func (l Lottery) Name() string {
	return l.myname
}

h.RegisterCheck(
	Lottery{myname: "custom.lottery.check", probability:0.3},
	gosundheit.InitialDelay(1*time.Second),
	gosundheit.ExecutionPeriod(30*time.Second),
	gosundheit.ExecutionTimeout(5*time.Second),
)

暴露健康检查端点

库提供了一个 HTTP 处理函数,用于以 JSON 格式提供健康统计数据:

http.Handle("/admin/health.json", healthhttp.HandleHealthJSON(h))

端点可以这样调用:

~ $ curl -i http://localhost:8080/admin/health.json
HTTP/1.1 503 Service Unavailable
Content-Type: application/json
Date: Tue, 22 Jan 2019 09:31:46 GMT
Content-Length: 701

{
	"custom.lottery.check": {
		"message": "lottery=0.206583",
		"error": {
			"message": "Sorry, I failed"
		},
		"timestamp": "2019-01-22T11:31:44.632415432+02:00",
		"num_failures": 2,
		"first_failure_time": "2019-01-22T11:31:41.632400256+02:00"
	},
	"lottery.check": {
		"message": "lottery=0.865335",
		"timestamp": "2019-01-22T11:31:44.63244047+02:00",
		"num_failures": 0,
		"first_failure_time": null
	},
	"url.check": {
		"message": "http://httpbin.org/status/200,300",
		"error": {
			"message": "unexpected status code: '300' expected: '200'"
		},
		"timestamp": "2019-01-22T11:31:44.632442937+02:00",
		"num_failures": 4,
		"first_failure_time": "2019-01-22T11:31:38.632485339+02:00"
	}
}

检查监听器

有时需要跟踪检查执行并应用自定义逻辑。例如,你可能希望添加日志记录,或为检查添加外部指标,或者在检查连续失败3次后触发一些恢复逻辑。

type checkEventsLogger struct{}

func (l checkEventsLogger) OnCheckRegistered(name string, res gosundheit.Result) {
	log.Printf("Check %q registered with initial result: %v\n", name, res)
}

func (l checkEventsLogger) OnCheckStarted(name string) {
	log.Printf("Check %q started...\n", name)
}

func (l checkEventsLogger) OnCheckCompleted(name string, res gosundheit.Result) {
	log.Printf("Check %q completed with result: %v\n", name, res)
}

h := gosundheit.New(gosundheit.WithCheckListeners(&checkEventsLogger))

健康监听器

有时需要跟踪已注册检查结果的变化。例如,你可能希望记录监控的结果数量,或发送这些结果的指标。

type healthLogger struct{}

func (l healthLogger) OnResultsUpdated(results map[string]Result) {
	log.Printf("There are %d results, general health is %t\n", len(results), allHealthy(results))
}

h := gosundheit.New(gosundheit.WithHealthListeners(&checkHealthLogger))

指标

库可以使用 CheckListener 公开指标。目前支持 OpenCensus 并公开以下指标:

  • health/check_status_by_name - 采样时的聚合健康状态指标
  • health/check_count_by_name_and_status - 检查的通过/失败计数
  • health/executeTime - 执行检查所需的时间

视图可以这样注册:

import (
	"github.com/AppsFlyer/go-sundheit"
	"github.com/AppsFlyer/go-sundheit/opencensus"
	"go.opencensus.io/stats/view"
)
// 这个监听器既可以作为检查监听器也可以作为健康监听器来报告指标
oc := opencensus.NewMetricsListener()
h := gosundheit.New(gosundheit.WithCheckListeners(oc), gosundheit.WithHealthListeners(oc))
// ...
view.Register(opencensus.DefaultHealthViews...)
// 或者注册单个视图
view.Register(opencensus.ViewCheckExecutionTime, opencensus.ViewCheckStatusByName, ...)

更多关于golang异步服务健康检查库go-sundheit的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang异步服务健康检查库go-sundheit的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


go-sundheit 异步健康检查库使用指南

go-sundheit 是 Uber 开源的一个 Go 语言健康检查库,它提供了异步健康检查机制,特别适合微服务架构中的健康检查需求。

主要特性

  • 异步执行健康检查
  • 支持并发健康检查
  • 可配置的检查间隔
  • 支持初始延迟
  • 提供健康状态聚合
  • 与标准库兼容

安装

go get github.com/AppsFlyer/go-sundheit

基本用法

1. 创建健康检查器

import (
	"github.com/AppsFlyer/go-sundheit"
	"github.com/AppsFlyer/go-sundheit/checks"
	"time"
)

func main() {
	h := gosundheit.New()
	
	// 在这里添加健康检查...
}

2. 添加健康检查

// 添加一个简单的HTTP健康检查
httpCheck, err := checks.NewHTTPCheck(
	checks.HTTPCheckConfig{
		CheckName: "api.health.check",
		Timeout:   5 * time.Second,
		URL:       "http://localhost:8080/health",
	},
)
if err != nil {
	panic(err)
}

err = h.RegisterCheck(
	httpCheck,
	gosundheit.InitialDelay(0),          // 立即开始检查
	gosundheit.ExecutionPeriod(10*time.Second), // 每10秒检查一次
)
if err != nil {
	panic(err)
}

3. 自定义健康检查

// 自定义检查函数
customCheck := checks.NewCheckFunc(
	"custom.check",
	func() (details interface{}, err error) {
		// 实现你的检查逻辑
		// 返回nil表示健康
		// 返回错误表示不健康
		return nil, nil
	},
)

err = h.RegisterCheck(
	customCheck,
	gosundheit.InitialDelay(2*time.Second),
	gosundheit.ExecutionPeriod(30*time.Second),
)

4. 获取健康状态

// 获取所有健康检查结果
results, healthy := h.GetResults()

// 检查特定健康检查的状态
if res, found := results["api.health.check"]; found {
	if res.Error != nil {
		// 处理错误
	}
}

// 检查整体健康状况
if !healthy {
	// 服务不健康
}

高级用法

1. 监听健康状态变化

// 创建监听器
listener := func(name string, result gosundheit.Result) {
	if result.Error != nil {
		fmt.Printf("Check '%s' failed: %v\n", name, result.Error)
	} else {
		fmt.Printf("Check '%s' succeeded\n", name)
	}
}

// 注册监听器
h.WithCheckListener(listener)

2. 数据库健康检查示例

import (
	"database/sql"
	_ "github.com/lib/pq"
)

func createDBCheck(db *sql.DB) (gosundheit.Check, error) {
	return checks.NewPingCheck("postgres.health.check", db, 1*time.Second)
}

// 使用
db, err := sql.Open("postgres", "postgres://user:pass@localhost/db")
if err != nil {
	panic(err)
}

dbCheck, err := createDBCheck(db)
if err != nil {
	panic(err)
}

err = h.RegisterCheck(
	dbCheck,
	gosundheit.InitialDelay(1*time.Second),
	gosundheit.ExecutionPeriod(15*time.Second),
)

3. 集成到HTTP服务

import (
	"net/http"
)

func healthHandler(h gosundheit.Health) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		_, healthy := h.GetResults()
		if !healthy {
			w.WriteHeader(http.StatusServiceUnavailable)
			return
		}
		w.WriteHeader(http.StatusOK)
	}
}

// 在main函数中使用
http.HandleFunc("/health", healthHandler(h))
http.ListenAndServe(":8080", nil)

最佳实践

  1. 合理设置检查间隔:根据检查的资源和重要性设置不同的检查频率
  2. 设置初始延迟:给依赖服务启动时间
  3. 分类健康检查:将关键检查和非关键检查分开
  4. 添加超时:避免健康检查阻塞
  5. 记录检查失败:通过监听器记录失败情况

性能考虑

  • go-sundheit 的设计是异步的,不会阻塞主流程
  • 检查在单独的goroutine中运行
  • 检查结果会被缓存,直到下一次检查运行
  • 避免在检查函数中执行耗时操作

通过合理使用 go-sundheit,你可以为你的微服务构建一个可靠的健康检查系统,及时发现和报告服务问题。

回到顶部