Thanos源码专题【左扬精讲】——Thanos API 组件(release-0.26)源码阅读和分析(详解 pkg\api\blocks\v1.go)

Thanos API 组件(release-0.26)源码阅读和分析(详解 pkg\api\blocks\v1.go)

https://github.com/thanos-io/thanos/blob/v0.26.0/pkg/api/blocks/v1.go

// Copyright (c) The Thanos Authors.
// Licensed under the Apache License 2.0.

package v1

import (
	"net/http"
	"time"

	"github.com/go-kit/log"                                    // 日志库
	"github.com/oklog/ulid"                                    // ulid库
	"github.com/opentracing/opentracing-go"                    // opentracing库,用于分布式追踪
	"github.com/pkg/errors"                                    // 错误处理库
	"github.com/prometheus/client_golang/prometheus"           // prometheus库,用于监控和度量指标收集
	"github.com/prometheus/client_golang/prometheus/promauto"  // promauto库,用于自动注册度量指标
	"github.com/prometheus/common/route"                       // route库,用于路由管理
	"github.com/thanos-io/thanos/pkg/api"                      // api库,用于处理API请求和响应
	"github.com/thanos-io/thanos/pkg/block"                    // block库,用于处理块数据
	"github.com/thanos-io/thanos/pkg/block/metadata"           // metadata库,用于处理块元数据
	extpromhttp "github.com/thanos-io/thanos/pkg/extprom/http" // extpromhttp库,用于扩展Prometheus HTTP客户端功能
	"github.com/thanos-io/thanos/pkg/logging"                  // logging库,用于处理日志记录
	"github.com/thanos-io/thanos/pkg/objstore"                 // objstore库,用于处理对象存储
)

// BlocksAPI is a very simple API used by Thanos Block Viewer.
// BlocksAPI 是一个用于Thanos Block Viewer的非常简单的API。
type BlocksAPI struct {
	baseAPI          *api.BaseAPI    // 嵌入了api.BaseAPI,用于处理一些基础API操作
	logger           log.Logger      // 日志记录器,用于记录日志信息
	globalBlocksInfo *BlocksInfo     // 全局块信息
	loadedBlocksInfo *BlocksInfo     // 加载的块信息
	disableCORS      bool            // 是否禁用CORS
	bkt              objstore.Bucket // 对象存储桶,用于存储和检索块数据
}

// BlocksInfo 是块信息结构体,包含了标签、块元数据和刷新时间等信息。
type BlocksInfo struct {
	Label       string          `json:"label"`       // 标签,用于标识块信息
	Blocks      []metadata.Meta `json:"blocks"`      // 块元数据,包含了块的详细信息
	RefreshedAt time.Time       `json:"refreshedAt"` // 刷新时间,表示块信息最后一次刷新的时间
	Err         error           `json:"err"`         // 错误信息,如果获取块信息时发生错误,则包含具体的错误
}

// ActionType int32 枚举类型,表示不同的操作类型。
type ActionType int32

const (
	Deletion     ActionType = iota // 删除操作
	NoCompaction                   // 不压缩操作
	Unknown                        // 未知操作
)

// parse 函数根据传入的字符串 s 返回对应的 ActionType 类型
//
// 参数:
//
//	s string: 待解析的字符串
//
// 返回值:
//
//	ActionType: 返回对应的 ActionType 类型
func parse(s string) ActionType {
	// 根据传入的字符串s进行分支判断
	switch s {
	case "DELETION":
		// 当s为"DELETION"时,返回Deletion类型
		return Deletion
	case "NO_COMPACTION":
		// 当s为"NO_COMPACTION"时,返回NoCompaction类型
		return NoCompaction
	default:
		// 当s既不是"DELETION"也不是"NO_COMPACTION"时,返回Unknown类型
		return Unknown
	}
}

// NewBlocksAPI creates a simple API to be used by Thanos Block Viewer.
// NewBlocksAPI 创建一个新的 BlocksAPI 实例
//
// 参数:
// logger: 用于记录日志的 log.Logger 实例
// disableCORS: 是否禁用 CORS
// label: BlocksAPI 实例的标签
// flagsMap: 传递给 API 的标志映射
// bkt: 用于存储对象的 objstore.Bucket 实例
//
// 返回值:
// *BlocksAPI: 返回一个新的 BlocksAPI 实例
func NewBlocksAPI(logger log.Logger, disableCORS bool, label string, flagsMap map[string]string, bkt objstore.Bucket) *BlocksAPI {
	// 创建一个新的BlocksAPI实例
	return &BlocksAPI{
		// 创建baseAPI实例,传入logger, disableCORS, flagsMap参数
		baseAPI: api.NewBaseAPI(logger, disableCORS, flagsMap),
		// 传入logger参数
		logger: logger,
		// 创建globalBlocksInfo实例,并设置Blocks为空数组和Label为传入的label参数
		globalBlocksInfo: &BlocksInfo{
			Blocks: []metadata.Meta{},
			Label:  label,
		},
		// 创建loadedBlocksInfo实例,并设置Blocks为空数组和Label为传入的label参数
		loadedBlocksInfo: &BlocksInfo{
			Blocks: []metadata.Meta{},
			Label:  label,
		},
		// 传入disableCORS参数
		disableCORS: disableCORS,
		// 传入bkt参数
		bkt: bkt,
	}
}

// Register 注册BlocksAPI路由到路由器
//
// 参数:
//
//	r *route.Router: 路由器对象
//	tracer opentracing.Tracer: OpenTracing追踪器对象
//	logger log.Logger: 日志记录器对象
//	ins extpromhttp.InstrumentationMiddleware: 监控中间件对象
//	logMiddleware *logging.HTTPServerMiddleware: HTTP服务器中间件对象
func (bapi *BlocksAPI) Register(r *route.Router, tracer opentracing.Tracer, logger log.Logger, ins extpromhttp.InstrumentationMiddleware, logMiddleware *logging.HTTPServerMiddleware) {
	// 调用基类API的注册方法
	bapi.baseAPI.Register(r, tracer, logger, ins, logMiddleware)

	// 获取API的Instrumentation实例
	instr := api.GetInstr(tracer, logger, ins, logMiddleware, bapi.disableCORS)

	// 注册GET请求处理函数
	// 路径为"/blocks",处理函数为bapi.blocks
	r.Get("/blocks", instr("blocks", bapi.blocks))

	// 注册POST请求处理函数
	// 路径为"/blocks/mark",处理函数为bapi.markBlock
	r.Post("/blocks/mark", instr("blocks_mark", bapi.markBlock))
}

// markBlock 函数用于标记一个块(Block)执行特定的操作。
//
// 参数:
// r: *http.Request 类型,包含请求信息的HTTP请求对象。
//
// 返回值:
// interface{}: 无返回值,故为nil。
// []error: 错误列表,这里不返回错误,故为nil。
// *api.ApiError: API错误对象,当发生错误时返回。
func (bapi *BlocksAPI) markBlock(r *http.Request) (interface{}, []error, *api.ApiError) {
	// 获取请求参数
	idParam := r.FormValue("id")         // 获取请求参数中的"id",表示要操作的块的ID
	actionParam := r.FormValue("action") // 获取请求参数中的"action",表示要执行的操作类型
	detailParam := r.FormValue("detail") // 获取请求参数中的"detail",表示操作的详细信息,例如原因或备注

	// 检查ID是否为空
	if idParam == "" {
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.New("ID cannot be empty")}
	}

	// 检查Action是否为空
	if actionParam == "" {
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.New("Action cannot be empty")}
	}

	// 解析ULID
	id, err := ulid.Parse(idParam)
	if err != nil {
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("ULID %q is not valid: %v", idParam, err)}
	}

	// 解析Action类型
	actionType := parse(actionParam)
	switch actionType {
	case Deletion:
		// 执行删除操作
		err := block.MarkForDeletion(r.Context(), bapi.logger, bapi.bkt, id, detailParam, promauto.With(nil).NewCounter(prometheus.CounterOpts{}))
		if err != nil {
			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
		}
	case NoCompaction:
		// 执行不压缩操作
		err := block.MarkForNoCompact(r.Context(), bapi.logger, bapi.bkt, id, metadata.ManualNoCompactReason, detailParam, promauto.With(nil).NewCounter(prometheus.CounterOpts{}))
		if err != nil {
			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
		}
	default:
		// 返回不支持的操作错误
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("not supported marker %v", actionParam)}
	}
	return nil, nil, nil
}

// blocks 函数用于处理BlocksAPI的blocks请求。
//
// 参数:
//
//	r *http.Request: HTTP请求对象
//
// 返回值:
//
//	interface{}: 返回的响应数据
//	[]error: 返回的错误列表
//	*api.ApiError: API错误对象
func (bapi *BlocksAPI) blocks(r *http.Request) (interface{}, []error, *api.ApiError) {
	// 获取URL查询参数中的"view"参数
	viewParam := r.URL.Query().Get("view")

	// 如果"view"参数值为"loaded"
	if viewParam == "loaded" {
		// 返回已加载的区块信息
		return bapi.loadedBlocksInfo, nil, nil
	}

	// 返回全局区块信息
	return bapi.globalBlocksInfo, nil, nil
}

// set 方法用于更新 BlocksInfo 结构体中的数据
//
// 参数:
// - blocks:[]metadata.Meta 类型的切片,包含要更新的区块数据
// - err:error 类型,包含错误信息,如果更新成功则为 nil
func (b *BlocksInfo) set(blocks []metadata.Meta, err error) {
	// 判断是否存在错误
	if err != nil {
		// 如果存在错误,则保持上次视图不变
		// Last view is maintained.
		b.RefreshedAt = time.Now()
		b.Err = err
		return
	}

	// 更新视图时间
	b.RefreshedAt = time.Now()
	// 更新区块数据
	b.Blocks = blocks
	// 设置错误信息
	b.Err = err
}

// SetGlobal updates the global blocks' metadata in the API.
// SetGlobal 设置全局块信息
//
// 参数:
//
//	blocks - []metadata.Meta:块信息的切片
//	err - error:错误信息
//
// 说明:
//
//	将blocks和err参数传递给bapi.globalBlocksInfo的set方法,用于设置全局块信息
func (bapi *BlocksAPI) SetGlobal(blocks []metadata.Meta, err error) {
	// 设置全局块信息
	// 将blocks和err参数传递给bapi.globalBlocksInfo的set方法
	bapi.globalBlocksInfo.set(blocks, err)
}

// SetLoaded 设置 BlocksAPI 实例的已加载区块信息
//
// 参数:
// - blocks []metadata.Meta:已加载的区块元数据列表
// - err error:加载区块过程中出现的错误
func (bapi *BlocksAPI) SetLoaded(blocks []metadata.Meta, err error) {
	// 调用 loadedBlocksInfo 的 set 方法,将 blocks 和 err 传入
	bapi.loadedBlocksInfo.set(blocks, err)
}
posted @ 2025-02-13 16:19  左扬  阅读(14)  评论(0)    收藏  举报