Files

163 lines
3.7 KiB
Go

package health
import (
"context"
"fmt"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
// S3Target S3健康检查目标
type S3Target struct {
ID string
Bucket string
Endpoint string
Client *s3.Client
}
// GetID 实现 Target 接口
func (t *S3Target) GetID() string {
return t.ID
}
// GetType 实现 Target 接口
func (t *S3Target) GetType() string {
return "s3"
}
// GetEndpoint 实现 Target 接口
func (t *S3Target) GetEndpoint() string {
return t.Endpoint
}
// S3Checker S3健康检查器
type S3Checker struct {
config Config
opRecorder OperationRecorder
}
// NewS3Checker 创建S3健康检查器
func NewS3Checker(config Config) *S3Checker {
if config.Interval == 0 {
config.Interval = 30 * time.Second
}
if config.Timeout == 0 {
config.Timeout = 5 * time.Second
}
if config.Retries == 0 {
config.Retries = 1
}
return &S3Checker{
config: config,
}
}
// SetOperationRecorder 设置操作记录器
func (c *S3Checker) SetOperationRecorder(recorder OperationRecorder) {
c.opRecorder = recorder
}
// Check 执行S3健康检查
func (c *S3Checker) Check(ctx context.Context, target Target) Status {
s3Target, ok := target.(*S3Target)
if !ok {
return Status{
Healthy: false,
LastChecked: time.Now(),
Message: "invalid target type for S3 checker",
Error: fmt.Errorf("expected *S3Target, got %T", target),
}
}
// 创建带超时的context
checkCtx, cancel := context.WithTimeout(ctx, c.config.Timeout)
defer cancel()
var lastErr error
for i := 0; i < c.config.Retries; i++ {
if i > 0 {
// 重试前等待
select {
case <-ctx.Done():
return Status{
Healthy: false,
LastChecked: time.Now(),
Message: "health check cancelled",
Error: ctx.Err(),
}
case <-time.After(time.Second):
}
}
err := c.performCheck(checkCtx, s3Target)
if err == nil {
return Status{
Healthy: true,
LastChecked: time.Now(),
Message: fmt.Sprintf("S3 bucket %s is healthy", s3Target.Bucket),
}
}
lastErr = err
}
return Status{
Healthy: false,
LastChecked: time.Now(),
Message: fmt.Sprintf("S3 bucket %s is unhealthy after %d retries", s3Target.Bucket, c.config.Retries),
Error: lastErr,
}
}
func (c *S3Checker) performCheck(ctx context.Context, target *S3Target) error {
switch c.config.Strategy {
case StrategyDetailed:
return c.performDetailedCheck(ctx, target)
default:
return c.performSimpleCheck(ctx, target)
}
}
// performSimpleCheck 执行简单健康检查(轻量级)
func (c *S3Checker) performSimpleCheck(ctx context.Context, target *S3Target) error {
// 尝试列出1个对象,这是最轻量级的检查方式
_, err := target.Client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
Bucket: aws.String(target.Bucket),
MaxKeys: aws.Int32(1),
})
// 记录操作(ListObjectsV2 是 Class A 操作)
if c.opRecorder != nil {
c.opRecorder.RecordOperation(target.GetID(), OperationTypeA)
}
return err
}
// performDetailedCheck 执行详细健康检查(包含更多验证)
func (c *S3Checker) performDetailedCheck(ctx context.Context, target *S3Target) error {
// 先执行简单检查
if err := c.performSimpleCheck(ctx, target); err != nil {
return err
}
// 可以添加更多详细检查,比如:
// 1. 检查存储桶策略
// 2. 测试写入权限(创建并删除测试对象)
// 3. 检查存储桶版本控制状态
// 4. 测量响应时间等性能指标
return nil
}
// GetInterval 获取检查间隔
func (c *S3Checker) GetInterval() time.Duration {
return c.config.Interval
}
// GetTimeout 获取检查超时时间
func (c *S3Checker) GetTimeout() time.Duration {
return c.config.Timeout
}