Thanos源码专题【左扬精讲】——Thanos Receive 组件(release-0.26)源码阅读和分析(详解 cmd/receive.go)

Thanos Receive 组件(release-0.26)源码阅读和分析(详解 cmd/receive.go)

https://github.com/thanos-io/thanos/blob/v0.26.0/cmd/thanos/receive.go

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

package main

import (
	"context"   // context上下文包
	"io/ioutil" // ioutil包用于读写文件
	"os"        // os包提供了一些基本的操作系统功能
	"path"      // path包提供了处理文件路径的实用函数
	"time"      // time包提供了时间的处理

	extflag "github.com/efficientgo/tools/extkingpin"                                   // extkingpin包提供了扩展的命令行参数解析功能
	"github.com/go-kit/log"                                                             // log包提供了日志记录的功能
	"github.com/go-kit/log/level"                                                       // level包提供了日志级别的功能
	grpc_logging "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" // grpc_logging包提供了gRPC日志记录的功能
	"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/tags"                 // tags包提供了gRPC标签记录的功能
	"github.com/oklog/run"                                                              // run包提供了并发执行多个goroutine的功能
	"github.com/opentracing/opentracing-go"                                             // opentracing包提供了分布式追踪的功能
	"github.com/pkg/errors"                                                             // errors包提供了错误处理的功能
	"github.com/prometheus/client_golang/prometheus"                                    // prometheus包提供了Prometheus监控的功能
	"github.com/prometheus/client_golang/prometheus/promauto"                           // promauto包提供了自动注册Prometheus指标的功能
	"github.com/prometheus/common/model"                                                // model包提供了Prometheus数据模型的功能
	"github.com/prometheus/prometheus/model/labels"                                     // labels包提供了Prometheus标签的功能
	"github.com/prometheus/prometheus/tsdb"                                             // tsdb包提供了Prometheus存储库的功能

	"github.com/thanos-io/thanos/pkg/block/metadata"         // metadata包提供了Prometheus块元数据的功能
	"github.com/thanos-io/thanos/pkg/component"              // component包提供了组件的功能
	"github.com/thanos-io/thanos/pkg/exemplars"              // exemplars包提供了Prometheus exemplar的功能
	"github.com/thanos-io/thanos/pkg/extgrpc"                // extgrpc包提供了gRPC扩展的功能
	"github.com/thanos-io/thanos/pkg/extkingpin"             // extkingpin包提供了扩展的命令行参数解析功能
	"github.com/thanos-io/thanos/pkg/extprom"                // extprom包提供了Prometheus扩展的功能
	"github.com/thanos-io/thanos/pkg/info"                   // info包提供了Thanos组件的信息功能
	"github.com/thanos-io/thanos/pkg/info/infopb"            // infopb包提供了Thanos组件信息的功能
	"github.com/thanos-io/thanos/pkg/logging"                // logging包提供了日志记录的功能
	"github.com/thanos-io/thanos/pkg/objstore"               // objstore包提供了对象存储的功能
	"github.com/thanos-io/thanos/pkg/objstore/client"        // client包提供了对象存储客户端的功能
	"github.com/thanos-io/thanos/pkg/prober"                 // prober包提供了探针的功能
	"github.com/thanos-io/thanos/pkg/receive"                // receive包提供了Thanos接收器功能
	"github.com/thanos-io/thanos/pkg/runutil"                // runutil包提供了运行时工具的功能
	grpcserver "github.com/thanos-io/thanos/pkg/server/grpc" // grpcserver包提供了gRPC服务器功能
	httpserver "github.com/thanos-io/thanos/pkg/server/http" // httpserver包提供了HTTP服务器功能
	"github.com/thanos-io/thanos/pkg/store"                  // store包提供了Thanos存储功能
	"github.com/thanos-io/thanos/pkg/store/labelpb"          // labelpb包提供了标签协议的功能
	"github.com/thanos-io/thanos/pkg/tls"                    // tls包提供了TLS功能的封装
)

// registerReceive 函数用于注册接收命令,以便通过Prometheus的远程写入API接收数据并将其写入本地tsdb。
func registerReceive(app *extkingpin.App) {
	// 创建一个接收命令,接收Prometheus的远程写入API请求并将其写入本地tsdb。
	cmd := app.Command(component.Receive.String(), "Accept Prometheus remote write API requests and write to local tsdb.")

	// 初始化接收配置
	conf := &receiveConfig{}
	conf.registerFlag(cmd)

	// 设置接收命令的初始化逻辑
	cmd.Setup(func(g *run.Group, logger log.Logger, reg *prometheus.Registry, tracer opentracing.Tracer, _ <-chan struct{}, _ bool) error {
		// 解析标签字符串
		lset, err := parseFlagLabels(conf.labelStrs)
		if err != nil {
			return errors.Wrap(err, "parse labels")
		}

		// 检查租户标签名称格式是否有效
		if !model.LabelName.IsValid(model.LabelName(conf.tenantLabelName)) {
			return errors.Errorf("unsupported format for tenant label name, got %s", conf.tenantLabelName)
		}
		// 检查是否配置了外部标签
		if len(lset) == 0 {
			// 返回错误,提示没有配置外部标签,这对于接收组件来说是必须的。如果需要,请使用`--receive.external-labels`标志配置它们。
			return errors.New("no external labels configured for receive, uniquely identifying external labels must be configured (ideally with `receive_` prefix); see https://thanos.io/tip/thanos/storage.md#external-labels for details.")
		}

		// 解析请求日志配置
		tagOpts, grpcLogOpts, err := logging.ParsegRPCOptions("", conf.reqLogConfig)
		if err != nil {
			return errors.Wrap(err, "error while parsing config for request logging")
		}

		// 配置TSDB选项
		tsdbOpts := &tsdb.Options{
			MinBlockDuration:       int64(time.Duration(*conf.tsdbMinBlockDuration) / time.Millisecond), // 最小块持续时间
			MaxBlockDuration:       int64(time.Duration(*conf.tsdbMaxBlockDuration) / time.Millisecond), // 最大块持续时间
			RetentionDuration:      int64(time.Duration(*conf.retention) / time.Millisecond),            // 保留持续时间
			NoLockfile:             conf.noLockFile,                                                     // 是否禁用锁文件
			WALCompression:         conf.walCompression,                                                 // WAL压缩是否启用
			AllowOverlappingBlocks: conf.tsdbAllowOverlappingBlocks,                                     // 是否允许重叠块
			MaxExemplars:           conf.tsdbMaxExemplars,                                               // 最大示例数
			EnableExemplarStorage:  true,                                                                // 是否启用示例存储
		}

		// 确定运行模式
		// Are we running in IngestorOnly, RouterOnly or RouterIngestor mode?
		receiveMode := conf.determineMode()

		// 运行接收逻辑
		return runReceive(
			g,                    // run.Group实例,用于并发执行多个goroutine
			logger,               // 日志记录器
			reg,                  // Prometheus注册表
			tracer,               // opentracing跟踪器
			grpcLogOpts, tagOpts, // gRPC日志选项和标签选项
			tsdbOpts,                         // TSDB选项
			lset,                             // 标签集
			component.Receive,                // 组件名称
			metadata.HashFunc(conf.hashFunc), // 哈希函数
			receiveMode,                      // 接收模式
			conf,                             // 接收配置
		)
	})
}

// runReceive 函数用于启动一个接收组件,处理接收到的数据并将其存储到本地TSDB或上传到对象存储中。
//
// 参数:
// - g: run.Group 对象,用于管理生命周期。
// - logger: log.Logger 对象,用于记录日志。
// - reg: prometheus.Registry 对象,用于注册指标。
// - tracer: opentracing.Tracer 对象,用于分布式追踪。
// - grpcLogOpts: grpc_logging.Option 切片,用于配置 gRPC 日志。
// - tagOpts: tags.Option 切片,用于配置标签选项。
// - tsdbOpts: tsdb.Options 对象,用于配置 TSDB 选项。
// - lset: labels.Labels 对象,用于配置标签集。
// - comp: component.SourceStoreAPI 对象,用于访问源存储 API。
// - hashFunc: metadata.HashFunc 对象,用于计算哈希值。
// - receiveMode: receive.ReceiverMode 对象,表示接收模式。
// - conf: *receiveConfig 对象,包含接收组件的配置信息。
//
// 返回值:
// - error: 如果函数执行过程中出现错误,则返回错误信息;否则返回 nil。
func runReceive(
	g *run.Group, // run.Group 对象,用于管理生命周期
	logger log.Logger, // 日志记录器
	reg *prometheus.Registry, // Prometheus 注册表
	tracer opentracing.Tracer, // 分布式追踪器
	grpcLogOpts []grpc_logging.Option, // gRPC 日志选项
	tagOpts []tags.Option, // 标签选项
	tsdbOpts *tsdb.Options, // TSDB 选项
	lset labels.Labels, // 标签集合
	comp component.SourceStoreAPI, // 组件源存储 API
	hashFunc metadata.HashFunc, // 哈希函数
	receiveMode receive.ReceiverMode, // 接收模式
	conf *receiveConfig, // 接收配置对象
) error {
	logger = log.With(logger, "component", "receive") // 添加组件标签到日志记录器

	level.Info(logger).Log("mode", receiveMode, "msg", "running receive") // 记录接收模式和运行状态

	rwTLSConfig, err := tls.NewServerConfig(log.With(logger, "protocol", "HTTP"), conf.rwServerCert, conf.rwServerKey, conf.rwServerClientCA) // 创建接收器TLS配置
	if err != nil {
		return err
	}

	// 创建 dialOpts,用于配置 gRPC 客户端, 包括日志记录器、注册表和追踪器等配置信息。
	dialOpts, err := extgrpc.StoreClientGRPCOpts(
		logger,                   // 日志记录器
		reg,                      // Prometheus 注册表
		tracer,                   // 分布式追踪器
		*conf.grpcCert != "",     // 是否使用 gRPC 证书
		*conf.grpcClientCA == "", // 是否使用 gRPC 客户端 CA
		conf.rwClientCert,        // 客户端证书
		conf.rwClientKey,         // 客户端密钥
		conf.rwClientServerCA,    // 服务器 CA
		conf.rwClientServerName,  // 服务器名称
	)
	if err != nil {
		return err
	}

	// 创建对象存储客户端
	var bkt objstore.Bucket

	// 创建对象存储客户端配置内容,用于后续创建对象存储客户端实例。
	confContentYaml, err := conf.objStoreConfig.Content()
	if err != nil {
		return err
	}

	// Has this thanos receive instance been configured to ingest metrics into a local TSDB?
	// thanos receive 实例是否配置为将指标摄取到本地 TSDB?
	enableIngestion := receiveMode == receive.IngestorOnly || receiveMode == receive.RouterIngestor

	// 判断是否启用上传功能,即是否有有效的对象存储配置内容。
	upload := len(confContentYaml) > 0
	// enableIngestion 判断是否启用摄取功能,即是否配置了本地 TSDB 或对象存储。
	if enableIngestion {
		// upload 判断是否启用上传功能,即是否有有效的对象存储配置内容。
		if upload {
			// tsdbOpts.MinBlockDuration 和 tsdbOpts.MaxBlockDuration 必须相等,否则无法启用上传功能。
			if tsdbOpts.MinBlockDuration != tsdbOpts.MaxBlockDuration {
				// conf.ignoreBlockSize 标志用于忽略 min/max block duration 配置不一致的警告。
				if !conf.ignoreBlockSize {
					// 报错信息,提示 TSDB 的最大时间和最小时间不一致。需要禁用压缩功能(tsdb.min-block-duration = tsdb.max-block-duration)。
					return errors.Errorf("found that TSDB Max time is %d and Min time is %d. "+
						"Compaction needs to be disabled (tsdb.min-block-duration = tsdb.max-block-duration)", tsdbOpts.MaxBlockDuration, tsdbOpts.MinBlockDuration)
				}
				// 告警信息,提示 min/max block duration 配置不一致的警告。如果需要忽略此警告,请使用 --ignore-block-size 标志。
				level.Warn(logger).Log("msg", "flag to ignore min/max block duration flags differing is being used. If the upload of a 2h block fails and a tsdb compaction happens that block may be missing from your Thanos bucket storage.")
			}
			// The background shipper continuously scans the data directory and uploads
			// new blocks to object storage service.
			// 后台上传器持续扫描数据目录,并将新块上传到对象存储服务。
			bkt, err = client.NewBucket(logger, confContentYaml, reg, comp.String())
			if err != nil {
				return err
			}
		} else {
			// 如果没有配置支持的存储桶,则禁用上传功能。
			level.Info(logger).Log("msg", "no supported bucket was configured, uploads will be disabled")
		}
	}

	// TODO(brancz): remove after a couple of versions
	// Migrate non-multi-tsdb capable storage to multi-tsdb disk layout.
	// migrateLegacyStorage 函数用于迁移旧存储格式到新的多租户磁盘布局。如果迁移失败,则返回错误信息。
	if err := migrateLegacyStorage(logger, conf.dataDir, conf.defaultTenantID); err != nil {
		// 迁移旧存储格式到新的多租户磁盘布局失败,返回错误信息。
		return errors.Wrapf(err, "migrate legacy storage in %v to default tenant %v", conf.dataDir, conf.defaultTenantID)
	}

	// receive.NewMultiTSDB 函数创建一个新的 MultiTSDB 实例,用于处理多租户的 TSDB 数据。
	dbs := receive.NewMultiTSDB(
		conf.dataDir,               // 数据目录路径
		logger,                     // 日志记录器
		reg,                        // 注册表
		tsdbOpts,                   // TSDB 配置选项
		lset,                       // 标签集
		conf.tenantLabelName,       // 租户标签名称
		bkt,                        // 存储桶
		conf.allowOutOfOrderUpload, // 是否允许乱序上传
		hashFunc,                   // 哈希函数
	)
	// receive.NewWriter 函数创建一个新的 Writer 实例,用于处理接收到的数据并将其写入到 MultiTSDB 中。
	writer := receive.NewWriter(log.With(logger, "component", "receive-writer"), dbs)
	// receive.NewHandler 函数创建一个新的 Handler 实例,用于处理接收到的数据并将其转发到其他节点。
	webHandler := receive.NewHandler(log.With(logger, "component", "receive-handler"), &receive.Options{
		Writer:            writer,                              // Writer 实例,用于处理接收到的数据并将其写入到 MultiTSDB 中。
		ListenAddress:     conf.rwAddress,                      // ListenAddress 配置项,指定了接收数据的地址。
		Registry:          reg,                                 // 注册表
		Endpoint:          conf.endpoint,                       // Endpoint 配置项,指定了接收数据的端点。
		TenantHeader:      conf.tenantHeader,                   // TenantHeader 配置项,指定了租户的头部信息。
		DefaultTenantID:   conf.defaultTenantID,                // DefaultTenantID 配置项,指定了默认的租户 ID。
		ReplicaHeader:     conf.replicaHeader,                  // ReplicaHeader 配置项,指定了副本的头部信息。
		ReplicationFactor: conf.replicationFactor,              // ReplicationFactor 配置项,指定了副本的数量。
		ReceiverMode:      receiveMode,                         // ReceiverMode 配置项,指定了接收数据的模式。
		Tracer:            tracer,                              // Tracer 配置项,指定了追踪器。
		TLSConfig:         rwTLSConfig,                         // TLSConfig 配置项,指定了 TLS 配置。
		DialOpts:          dialOpts,                            // DialOpts 配置项,指定了拨号选项。
		ForwardTimeout:    time.Duration(*conf.forwardTimeout), // ForwardTimeout 配置项,指定了转发超时时间。
	})

	grpcProbe := prober.NewGRPC()   // 创建一个新的 GRPCProbe 实例,用于监控 gRPC 服务器的健康状态。
	httpProbe := prober.NewHTTP()   // 创建一个新的 HTTPProbe 实例,用于监控 HTTP 服务器的健康状态。
	statusProber := prober.Combine( // 创建一个新的 CombineProbe 实例,用于组合多个 Probe 实例。
		httpProbe, // 创建一个新的 HTTPProbe 实例,用于监控 HTTP 服务器的健康状态。
		grpcProbe, // 创建一个新的 GRPCProbe 实例,用于监控 gRPC 服务器的健康状态。
		prober.NewInstrumentation(comp, logger, extprom.WrapRegistererWithPrefix("thanos_", reg)), // 创建一个新的 InstrumentationProbe 实例,用于监控 Prometheus 的指标。
	)

	// Start all components while we wait for TSDB to open but only load
	// initial config and mark ourselves as ready after it completed.

	// reloadGRPCServer signals when - (1)TSDB is ready and the Store gRPC server can start.
	// (2) The Hashring files have changed if tsdb ingestion is disabled.
	reloadGRPCServer := make(chan struct{}, 1) // reloadGRPCServer 信号用于通知 TSDB 已准备好,并且 Store gRPC 服务器可以启动。
	// hashringChangedChan signals when TSDB needs to be flushed and updated due to hashring config change.
	hashringChangedChan := make(chan struct{}, 1) // hashringChangedChan 信号用于通知 TSDB 需要刷新和更新,因为哈希环配置发生了变化。
	// uploadC signals when new blocks should be uploaded.
	uploadC := make(chan struct{}, 1) // uploadC 信号用于通知新的块应该被上传。
	// uploadDone signals when uploading has finished.
	uploadDone := make(chan struct{}, 1) // uploadDone 信号用于通知上传已完成。

	// enableIngestion 当为 true 时,表示启用 TSDB 数据摄取。
	if enableIngestion {
		level.Debug(logger).Log("msg", "setting up tsdb")
		{
			// startTSDBAndUpload 函数用于启动 TSDB 并上传数据。
			if err := startTSDBAndUpload(g, logger, reg, dbs, reloadGRPCServer, uploadC, hashringChangedChan, upload, uploadDone, statusProber, bkt); err != nil {
				return err
			}
		}
	}
	// 启动哈希环的设置。hashringChangedChan 信号用于通知 TSDB 需要刷新和更新,因为哈希环配置发生了变化。
	level.Debug(logger).Log("msg", "setting up hashring")
	{
		// setupHashring 函数用于设置哈希环。
		if err := setupHashring(g, logger, reg, conf, hashringChangedChan, webHandler, statusProber, reloadGRPCServer, enableIngestion); err != nil {
			return err
		}
	}
	// 启动 HTTP 服务器的设置。
	level.Debug(logger).Log("msg", "setting up http server")
	{
		// srv 是一个 HTTPServer 实例,用于监听和服务的启动。
		srv := httpserver.New(
			logger,    // logger 是一个日志记录器,用于记录日志信息。
			reg,       // reg 是一个 Prometheus 注册表,用于注册和收集指标。
			comp,      // comp 是一个组件,用于提供存储和检索数据的功能。
			httpProbe, // httpProbe 是一个 HTTP 探针,用于检查 HTTP 服务器的健康状态。
			httpserver.WithListen(*conf.httpBindAddr),                        // WithListen 用于设置 HTTP 服务器的监听地址。
			httpserver.WithGracePeriod(time.Duration(*conf.httpGracePeriod)), // WithGracePeriod 用于设置优雅关闭的持续时间。
			httpserver.WithTLSConfig(*conf.httpTLSConfig),                    // WithTLSConfig 用于设置 HTTP 服务器的 TLS 配置。
		)
		g.Add(func() error {
			statusProber.Healthy()

			return srv.ListenAndServe()
		}, func(err error) {
			statusProber.NotReady(err)
			defer statusProber.NotHealthy(err)

			srv.Shutdown(err)
		})
	}
	// 启动 gRPC 服务器的设置。
	level.Debug(logger).Log("msg", "setting up grpc server")
	{
		// setupAndRunGRPCServer 函数用于设置和运行 gRPC 服务器。
		if err := setupAndRunGRPCServer(g, logger, reg, tracer, conf, reloadGRPCServer, comp, dbs, webHandler, grpcLogOpts,
			tagOpts, grpcProbe, httpProbe.IsReady); err != nil {
			return err
		}
	}

	// 启动 receive http handler,用于处理 HTTP 请求。
	level.Debug(logger).Log("msg", "setting up receive http handler")
	{
		g.Add(
			func() error {
				return errors.Wrap(webHandler.Run(), "error starting web server")
			},
			func(err error) {
				webHandler.Close()
			},
		)
	}

	level.Info(logger).Log("msg", "starting receiver")
	return nil
}

// setupAndRunGRPCServer sets up the configuration for the gRPC server.
// It also sets up a handler for reloading the server if tsdb reloads.
// setupAndRunGRPCServer 函数用于设置并运行 gRPC 服务器。
//
// 参数:
// - g *run.Group: 运行组,用于管理协程和错误处理。
// - logger log.Logger: 日志记录器,用于记录日志。
// - reg *prometheus.Registry: Prometheus 注册中心,用于注册和导出 Prometheus 指标。
// - tracer opentracing.Tracer: OpenTracing 追踪器,用于追踪请求。
// - conf *receiveConfig: 接收配置,包含 gRPC 相关的配置。
// - reloadGRPCServer chan struct{}: 通道,当需要重新加载 gRPC 服务器时发送信号。
// - comp component.SourceStoreAPI: SourceStoreAPI 组件,用于处理数据源存储。
// - dbs *receive.MultiTSDB: MultiTSDB 实例,包含多个 TSDB 实例。
// - webHandler *receive.Handler: HTTP 处理器,用于处理 HTTP 请求。
// - grpcLogOpts []grpc_logging.Option: gRPC 日志选项。
// - tagOpts []tags.Option: 标签选项。
// - grpcProbe *prober.GRPCProbe: gRPC 探测器,用于探测 gRPC 服务器的状态。
// - isReady func() bool: 判断服务器是否就绪的函数。
//
// 返回值:
// - error: 如果发生错误,则返回错误信息;否则返回 nil。
func setupAndRunGRPCServer(g *run.Group,
	logger log.Logger, // 日志记录器
	reg *prometheus.Registry, // Prometheus 注册中心
	tracer opentracing.Tracer, // OpenTracing 追踪器,用于追踪请求。
	conf *receiveConfig, // 接收配置,包含 gRPC 相关的配置。
	reloadGRPCServer chan struct{}, //	通道,当需要重新加载 gRPC 服务器时发送信号。
	comp component.SourceStoreAPI, // SourceStoreAPI 组件,用于处理数据源存储。
	dbs *receive.MultiTSDB, // MultiTSDB 实例,包含多个 TSDB 实例。
	webHandler *receive.Handler, // HTTP 处理器,用于处理 HTTP 请求。
	grpcLogOpts []grpc_logging.Option, // gRPC 日志选项。
	tagOpts []tags.Option, // 标签选项。
	grpcProbe *prober.GRPCProbe, // gRPC 探测器,用于探测 gRPC 服务器的状态。
	isReady func() bool, // 判断服务器是否就绪的函数。
) error {

	var s *grpcserver.Server
	// startGRPCListening 是一个通道,用于在接收到信号时重新启动 gRPC 服务器。
	// startGRPCListening re-starts the gRPC server once it receives a signal.
	startGRPCListening := make(chan struct{})

	g.Add(func() error {
		// 启动 gRPC 服务器。
		defer close(startGRPCListening)
		// 设置 gRPC 服务器的 TLS 配置。
		tlsCfg, err := tls.NewServerConfig(log.With(logger, "protocol", "gRPC"), *conf.grpcCert, *conf.grpcKey, *conf.grpcClientCA)
		if err != nil {
			// 如果设置 TLS 配置失败,则返回错误。
			return errors.Wrap(err, "setup gRPC server")
		}
		// 遍历 reloadGRPCServer 通道,直到收到信号。
		for range reloadGRPCServer {
			// 如果 gRPC 服务器已经启动,则关闭它。
			if s != nil {
				// 关闭 gRPC 服务器的优雅退出。
				s.Shutdown(errors.New("reload hashrings"))
			}
			// 创建一个新的 gRPC 服务器。
			mts := store.NewMultiTSDBStore(
				logger,         // 日志记录器。
				reg,            // 注册表。
				comp,           // 组件。
				dbs.TSDBStores, // TSDB 存储器。
			)
			// store.ReadWriteTSDBStore 实现了 StoreServer 和 WriteableStoreServer 接口。
			rw := store.ReadWriteTSDBStore{
				StoreServer:          mts,        // 实现了 StoreServer 和 WriteableStoreServer 接口的实例。
				WriteableStoreServer: webHandler, // HTTP 处理器,用于处理写入请求。
			}

			// info.NewInfoServer 创建一个新的 InfoServer 实例。
			infoSrv := info.NewInfoServer(
				component.Receive.String(), // 组件名称。
				info.WithLabelSetFunc(func() []labelpb.ZLabelSet { return mts.LabelSet() }), // 标签集函数。
				info.WithStoreInfoFunc(func() *infopb.StoreInfo { // 存储信息函数。
					if isReady() {
						minTime, maxTime := mts.TimeRange()
						return &infopb.StoreInfo{
							MinTime: minTime,
							MaxTime: maxTime,
						}
					}
					return nil
				}),
				// WithExemplarsInfoFunc 创建一个新的 ExemplarsInfoFunc 实例。
				info.WithExemplarsInfoFunc(),
			)
			// grpcserver.New 创建一个新的 gRPC 服务器。
			s = grpcserver.New(logger, &receive.UnRegisterer{Registerer: reg}, tracer, grpcLogOpts, tagOpts, comp, grpcProbe,
				grpcserver.WithServer(store.RegisterStoreServer(rw)),                                                // 注册 StoreServer。
				grpcserver.WithServer(store.RegisterWritableStoreServer(rw)),                                        // 注册 WritableStoreServer。
				grpcserver.WithServer(exemplars.RegisterExemplarsServer(exemplars.NewMultiTSDB(dbs.TSDBExemplars))), // 注册 ExemplarsServer。
				grpcserver.WithServer(info.RegisterInfoServer(infoSrv)),                                             // 注册 InfoServer。
				grpcserver.WithListen(*conf.grpcBindAddr),                                                           // 监听地址。
				grpcserver.WithGracePeriod(time.Duration(*conf.grpcGracePeriod)),                                    // 优雅关闭时间。
				grpcserver.WithTLSConfig(tlsCfg),                                                                    // TLS 配置。
				grpcserver.WithMaxConnAge(*conf.grpcMaxConnAge),                                                     // 最大连接时间。
			)
			// 启动 gRPC 服务器的优雅关闭。
			startGRPCListening <- struct{}{}
		}
		if s != nil {
			// 在 gRPC 服务器关闭时,我们需要优雅地关闭所有数据库。
			s.Shutdown(err)
		}
		return nil
	}, func(error) {})

	// 我们需要在数据库发生变化时能够启动和停止 gRPC 服务器,因此需要为它创建一个独立的运行组。
	// We need to be able to start and stop the gRPC server
	// whenever the DB changes, thus it needs its own run group.
	g.Add(func() error {
		for range startGRPCListening {
			level.Info(logger).Log("msg", "listening for StoreAPI and WritableStoreAPI gRPC", "address", *conf.grpcBindAddr)
			if err := s.ListenAndServe(); err != nil {
				return errors.Wrap(err, "serve gRPC")
			}
		}
		return nil
	}, func(error) {})

	return nil

}

// setupHashring sets up the hashring configuration provided.
// If no hashring is provided, we setup a single node hashring with local endpoint.
// setupHashring 用于设置并初始化 Hashring。
//
// 参数:
//   - g: *run.Group,用于管理运行的任务组。
//   - logger: log.Logger,用于记录日志。
//   - reg: *prometheus.Registry,用于注册 Prometheus 指标。
//   - conf: *receiveConfig,接收配置。
//   - hashringChangedChan: chan struct{},当 Hashring 发生变化时发送信号的通道。
//   - webHandler: *receive.Handler,用于处理 Web 请求的处理器。
//   - statusProber: prober.Probe,用于探测服务状态的探针。
//   - reloadGRPCServer: chan struct{},当 gRPC 服务器需要重新加载时发送信号的通道。
//   - enableIngestion: bool,是否启用数据摄取功能。
//
// 返回值:
//   - error,如果设置过程中发生错误则返回相应的错误信息,否则返回 nil。
func setupHashring(g *run.Group,
	logger log.Logger,
	reg *prometheus.Registry,
	conf *receiveConfig,
	hashringChangedChan chan struct{},
	webHandler *receive.Handler,
	statusProber prober.Probe,
	reloadGRPCServer chan struct{},
	enableIngestion bool,
) error {
	// Note: the hashring configuration watcher
	// is the sender and thus closes the chan.
	// In the single-node case, which has no configuration
	// watcher, we close the chan ourselves.
	updates := make(chan receive.Hashring, 1)

	// 配置文件的路径
	// 初始化配置观察者时给定的 Hashrings 配置文件路径
	if conf.hashringsFilePath != "" {
		cw, err := receive.NewConfigWatcher(log.With(logger, "component", "config-watcher"), reg, conf.hashringsFilePath, *conf.refreshInterval)
		if err != nil {
			return errors.Wrap(err, "failed to initialize config watcher")
		}

		// 在运行观察者之前检查 hashring 配置
		if err := cw.ValidateConfig(); err != nil {
			cw.Stop()
			close(updates)
			return errors.Wrap(err, "failed to validate hashring configuration file")
		}

		ctx, cancel := context.WithCancel(context.Background())
		g.Add(func() error {
			level.Info(logger).Log("msg", "the hashring initialized with config watcher.")
			// 从配置观察者获取 Hashring
			return receive.HashringFromConfigWatcher(ctx, updates, cw)
		}, func(error) {
			cancel()
		})
	} else {
		var (
			ring receive.Hashring
			err  error
		)
		// 通过内容初始化配置
		// The Hashrings config file content given initialize configuration from content.
		if len(conf.hashringsFileContent) > 0 {
			ring, err = receive.HashringFromConfig(conf.hashringsFileContent)
			if err != nil {
				close(updates)
				return errors.Wrap(err, "failed to validate hashring configuration file")
			}
			level.Info(logger).Log("msg", "the hashring initialized directly with the given content through the flag.")
		} else {
			level.Info(logger).Log("msg", "the hashring file is not specified use single node hashring.")
			// 使用单个节点 Hashring
			ring = receive.SingleNodeHashring(conf.endpoint)
		}

		cancel := make(chan struct{})
		g.Add(func() error {
			defer close(updates)
			// 将 Hashring 发送到更新通道
			updates <- ring
			<-cancel
			return nil
		}, func(error) {
			close(cancel)
		})
	}

	cancel := make(chan struct{})
	g.Add(func() error {

		if enableIngestion {
			defer close(hashringChangedChan)
		}

		for {
			select {
			case h, ok := <-updates:
				if !ok {
					return nil
				}
				// 更新 web 处理器的 Hashring
				webHandler.Hashring(h)
				msg := "hashring has changed; server is not ready to receive web requests"
				// 设置状态为未就绪
				statusProber.NotReady(errors.New(msg))
				level.Info(logger).Log("msg", msg)

				if enableIngestion {
					// 发送信号给 tsdb 重新加载,然后重启 gRPC 服务器
					// send a signal to tsdb to reload, and then restart the gRPC server.
					hashringChangedChan <- struct{}{}
				} else {
					// 不需要 tsdb 重新加载,直接重启 gRPC 服务器
					level.Info(logger).Log("msg", "server has reloaded, ready to start accepting requests")
					statusProber.Ready()
					reloadGRPCServer <- struct{}{}
				}
			case <-cancel:
				return nil
			}
		}
	}, func(err error) {
		close(cancel)
	},
	)
	return nil
}

// startTSDBAndUpload starts up the multi-tsdb and sets up the rungroup to flush the tsdb and reload on hashring change.
// It also uploads the tsdb to object store if upload is enabled.
// startTSDBAndUpload 函数启动TSDB并上传数据。
//
// 参数:
// - g: 运行组,用于管理并行执行的函数。
// - logger: 日志记录器,用于记录日志信息。
// - reg: Prometheus注册表,用于注册Prometheus指标。
// - dbs: MultiTSDB对象,表示多个TSDB的集合。
// - reloadGRPCServer: 通道,当TSDB重新加载完成时发送信号。
// - uploadC: 通道,当需要上传数据时发送信号。
// - hashringChangedChan: 通道,当hashring变化时发送信号。
// - upload: 布尔值,指示是否需要上传数据。
// - uploadDone: 通道,当上传完成时发送信号。
// - statusProber: 探测器,用于探测服务状态。
// - bkt: 对象存储桶,用于存储上传的数据。
//
// 返回值:
// - error: 如果发生错误,则返回错误信息;否则返回nil。
func startTSDBAndUpload(g *run.Group,
	logger log.Logger,
	reg *prometheus.Registry,
	dbs *receive.MultiTSDB,
	reloadGRPCServer chan struct{},
	uploadC chan struct{},
	hashringChangedChan chan struct{},
	upload bool,
	uploadDone chan struct{},
	statusProber prober.Probe,
	bkt objstore.Bucket,

) error {
	// 设置日志组件
	log.With(logger, "component", "storage")

	// 创建Prometheus计数器
	dbUpdatesStarted := promauto.With(reg).NewCounter(prometheus.CounterOpts{
		Name: "thanos_receive_multi_db_updates_attempted_total",
		Help: "Number of Multi DB attempted reloads with flush and potential upload due to hashring changes",
	})
	dbUpdatesCompleted := promauto.With(reg).NewCounter(prometheus.CounterOpts{
		Name: "thanos_receive_multi_db_updates_completed_total",
		Help: "Number of Multi DB completed reloads with flush and potential upload due to hashring changes",
	})

	// 删除存储锁文件
	level.Debug(logger).Log("msg", "removing storage lock files if any")
	// 删除存储锁文件,如果删除失败则返回错误
	if err := dbs.RemoveLockFilesIfAny(); err != nil {
		return errors.Wrap(err, "remove storage lock files")
	}

	// TSDBs 重新加载逻辑,监听hashring变化
	cancel := make(chan struct{})
	g.Add(func() error {
		defer close(reloadGRPCServer)
		defer close(uploadC)

		// 在退出前确保WAL被刷新且DBs被关闭
		defer func() {
			level.Info(logger).Log("msg", "shutting down storage")
			if err := dbs.Flush(); err != nil {
				level.Error(logger).Log("err", err, "msg", "failed to flush storage")
			} else {
				level.Info(logger).Log("msg", "storage is flushed successfully")
			}
			if err := dbs.Close(); err != nil {
				level.Error(logger).Log("err", err, "msg", "failed to close storage")
				return
			}
			level.Info(logger).Log("msg", "storage is closed")
		}()

		for {
			select {
			case <-cancel:
				return nil
			case _, ok := <-hashringChangedChan:
				if !ok {
					return nil
				}
				dbUpdatesStarted.Inc()
				level.Info(logger).Log("msg", "updating storage")

				if err := dbs.Flush(); err != nil {
					return errors.Wrap(err, "flushing storage")
				}
				if err := dbs.Open(); err != nil {
					return errors.Wrap(err, "opening storage")
				}
				if upload {
					uploadC <- struct{}{}
					<-uploadDone
				}
				statusProber.Ready()
				level.Info(logger).Log("msg", "storage started, and server is ready to receive web requests")
				dbUpdatesCompleted.Inc()
				reloadGRPCServer <- struct{}{}
			}
		}
	}, func(err error) {
		close(cancel)
	})

	// 如果需要上传,则执行上传逻辑
	if upload {
		logger := log.With(logger, "component", "uploader")
		upload := func(ctx context.Context) error {
			level.Debug(logger).Log("msg", "upload phase starting")
			start := time.Now()

			uploaded, err := dbs.Sync(ctx)
			if err != nil {
				level.Warn(logger).Log("msg", "upload failed", "elapsed", time.Since(start), "err", err)
				return err
			}
			level.Debug(logger).Log("msg", "upload phase done", "uploaded", uploaded, "elapsed", time.Since(start))
			return nil
		}
		{
			level.Info(logger).Log("msg", "upload enabled, starting initial sync")
			if err := upload(context.Background()); err != nil {
				return errors.Wrap(err, "initial upload failed")
			}
			level.Info(logger).Log("msg", "initial sync done")
		}
		{
			ctx, cancel := context.WithCancel(context.Background())
			g.Add(func() error {
				// 确保我们正确地清理一切
				defer func() {
					runutil.CloseWithLogOnErr(logger, bkt, "bucket client")
				}()

				// 在退出前确保所有块都被上传
				defer func() {
					<-uploadC // 由存储例程在完成时关闭
					level.Info(logger).Log("msg", "uploading the final cut block before exiting")
					ctx, cancel := context.WithCancel(context.Background())
					uploaded, err := dbs.Sync(ctx)
					if err != nil {
						cancel()
						level.Error(logger).Log("msg", "the final upload failed", "err", err)
						return
					}
					cancel()
					level.Info(logger).Log("msg", "the final cut block was uploaded", "uploaded", uploaded)
				}()

				defer close(uploadDone)

				// 在循环中运行上传器
				tick := time.NewTicker(30 * time.Second)
				defer tick.Stop()

				for {
					select {
					case <-ctx.Done():
						return nil
					case <-uploadC:
						// 按需上传
						if err := upload(ctx); err != nil {
							level.Warn(logger).Log("msg", "on demand upload failed", "err", err)
						}
						uploadDone <- struct{}{}
					case <-tick.C:
						if err := upload(ctx); err != nil {
							level.Warn(logger).Log("msg", "recurring upload failed", "err", err)
						}
					}
				}
			}, func(error) {
				cancel()
			})
		}
	}

	return nil
}

// migrateLegacyStorage 函数用于将遗留的存储迁移到新的多租户存储布局。
//
// 参数:
//
//	logger log.Logger: 日志记录器,用于记录迁移过程中的信息。
//	dataDir string: 数据目录的路径。
//	defaultTenantID string: 默认租户ID。
//
// 返回值:
//
//	error: 如果迁移过程中发生错误,则返回错误信息;否则返回nil。
func migrateLegacyStorage(logger log.Logger, dataDir, defaultTenantID string) error {
	// 将默认租户的数据目录设置为dataDir和defaultTenantID的拼接
	defaultTenantDataDir := path.Join(dataDir, defaultTenantID)

	// 检查默认租户的数据目录是否存在
	if _, err := os.Stat(defaultTenantDataDir); !os.IsNotExist(err) {
		// 如果默认租户的数据目录已经存在,则不进行存储迁移
		level.Info(logger).Log("msg", "default tenant data dir already present, not attempting to migrate storage")
		return nil
	}

	// 检查dataDir是否存在
	if _, err := os.Stat(dataDir); os.IsNotExist(err) {
		// 如果dataDir不存在,则不进行数据迁移
		level.Info(logger).Log("msg", "no existing storage found, no data migration attempted")
		return nil
	}

	// 输出日志,表示发现了遗留存储,并计划进行迁移
	level.Info(logger).Log("msg", "found legacy storage, migrating to multi-tsdb layout with default tenant", "defaultTenantID", defaultTenantID)

	// 读取dataDir目录下的所有文件和目录
	files, err := ioutil.ReadDir(dataDir)
	if err != nil {
		// 如果读取dataDir失败,则包装错误并返回
		return errors.Wrapf(err, "read legacy data dir: %v", dataDir)
	}

	// 创建默认租户的数据目录
	if err := os.MkdirAll(defaultTenantDataDir, 0750); err != nil {
		// 如果创建默认租户的数据目录失败,则包装错误并返回
		return errors.Wrapf(err, "create default tenant data dir: %v", defaultTenantDataDir)
	}

	// 遍历dataDir目录下的所有文件和目录
	for _, f := range files {
		// 从dataDir移动文件到默认租户的数据目录
		from := path.Join(dataDir, f.Name())
		to := path.Join(defaultTenantDataDir, f.Name())
		if err := os.Rename(from, to); err != nil {
			// 如果移动文件失败,则包装错误并返回
			return errors.Wrapf(err, "migrate file from %v to %v", from, to)
		}
	}

	return nil
}

type receiveConfig struct {
	httpBindAddr    *string         // 接收请求的HTTP绑定地址
	httpGracePeriod *model.Duration // HTTP优雅关闭的持续时间
	httpTLSConfig   *string         // HTTP TLS配置

	grpcBindAddr    *string         // 接收请求的gRPC绑定地址
	grpcGracePeriod *model.Duration // gRPC优雅关闭的持续时间
	grpcCert        *string         // gRPC证书
	grpcKey         *string         // gRPC私钥
	grpcClientCA    *string         // gRPC客户端CA证书
	grpcMaxConnAge  *time.Duration  // gRPC最大连接年龄

	rwAddress          string // 接收请求的地址
	rwServerCert       string // 读写服务器证书
	rwServerKey        string // 读写服务器私钥
	rwServerClientCA   string // 读写服务器客户端CA证书
	rwClientCert       string // 读写客户端证书
	rwClientKey        string // 读写客户端私钥
	rwClientServerCA   string // 读写客户端服务器CA证书
	rwClientServerName string // 读写客户端服务器名称

	dataDir   string   // 数据目录
	labelStrs []string // 标签字符串

	objStoreConfig *extflag.PathOrContent // 对象存储配置
	retention      *model.Duration        // 数据保留时间

	hashringsFilePath    string // 哈希环文件路径
	hashringsFileContent string // 哈希环文件内容

	refreshInterval   *model.Duration // 刷新间隔
	endpoint          string          // 端点
	tenantHeader      string          // 租户头部信息
	tenantLabelName   string          // 租户标签名称
	defaultTenantID   string          // 默认租户ID
	replicaHeader     string          // 副本头部信息
	replicationFactor uint64          // 复制因子
	forwardTimeout    *model.Duration // 转发超时时间

	tsdbMinBlockDuration       *model.Duration // 最小块持续时间
	tsdbMaxBlockDuration       *model.Duration // 最大块持续时间
	tsdbAllowOverlappingBlocks bool            // 允许重叠块
	tsdbMaxExemplars           int64           // 最大示例数

	walCompression bool // WAL压缩
	noLockFile     bool // 无锁文件

	hashFunc string // 哈希函数

	ignoreBlockSize       bool // 忽略块大小
	allowOutOfOrderUpload bool // 允许无序上传

	reqLogConfig *extflag.PathOrContent // 请求日志配置
}

// registerFlag 方法用于在指定的命令(cmd)上注册多个配置标志位,这些标志位用于配置接收组件的行为。
func (rc *receiveConfig) registerFlag(cmd extkingpin.FlagClause) {
	// 注册HTTP相关标志位
	rc.httpBindAddr, rc.httpGracePeriod, rc.httpTLSConfig = extkingpin.RegisterHTTPFlags(cmd)
	// 注册gRPC相关标志位
	rc.grpcBindAddr, rc.grpcGracePeriod, rc.grpcCert, rc.grpcKey, rc.grpcClientCA, rc.grpcMaxConnAge = extkingpin.RegisterGRPCFlags(cmd)

	// 注册远程写入地址标志位
	cmd.Flag("remote-write.address", "远程写入地址").
		Default("0.0.0.0:19291").StringVar(&rc.rwAddress)

	// 注册远程写入服务器TLS证书标志位
	cmd.Flag("remote-write.server-tls-cert", "HTTP服务器的TLS证书,留空则禁用TLS").Default("").StringVar(&rc.rwServerCert)

	// 注册远程写入服务器TLS密钥标志位
	cmd.Flag("remote-write.server-tls-key", "HTTP服务器的TLS密钥,留空则禁用TLS").Default("").StringVar(&rc.rwServerKey)

	// 注册远程写入服务器TLS客户端CA标志位
	cmd.Flag("remote-write.server-tls-client-ca", "TLS CA以验证客户端。如果没有指定客户端CA,则服务器端不进行客户端验证。(tls.NoClientCert)").Default("").StringVar(&rc.rwServerClientCA)

	// 注册远程写入客户端TLS证书标志位
	cmd.Flag("remote-write.client-tls-cert", "用于标识此客户端到服务器的TLS证书").Default("").StringVar(&rc.rwClientCert)

	// 注册远程写入客户端TLS密钥标志位
	cmd.Flag("remote-write.client-tls-key", "客户端证书的TLS密钥").Default("").StringVar(&rc.rwClientKey)

	// 注册远程写入客户端TLS CA证书标志位
	cmd.Flag("remote-write.client-tls-ca", "用于验证服务器的TLS CA证书").Default("").StringVar(&rc.rwClientServerCA)

	// 注册远程写入客户端服务器名称标志位
	cmd.Flag("remote-write.client-server-name", "要验证返回的TLS证书上的主机名的服务器名称。请参阅https://tools.ietf.org/html/rfc4366#section-3.1").Default("").StringVar(&rc.rwClientServerName)

	// 注册TSDB数据目录标志位
	cmd.Flag("tsdb.path", "TSDB的数据目录").
		Default("./data").StringVar(&rc.dataDir)

	// 注册外部标签标志位
	cmd.Flag("label", "要宣布的外部标签。当处理多个tsdb实例时,此标志将在未来被移除").PlaceHolder("key=\"value\"").StringsVar(&rc.labelStrs)

	// 注册通用对象存储标志位
	rc.objStoreConfig = extkingpin.RegisterCommonObjStoreFlags(cmd, "", false)

	// 注册TSDB保留时间标志位
	rc.retention = extkingpin.ModelDuration(cmd.Flag("tsdb.retention", "在本地存储上保留原始样本的时间。0d - 禁用此保留").Default("15d"))

	// 注册哈希环文件路径标志位
	cmd.Flag("receive.hashrings-file", "包含哈希环配置的文件的路径。初始化一个监视器以监视更改并动态更新哈希环").PlaceHolder("<path>").StringVar(&rc.hashringsFilePath)

	// 注册哈希环内容标志位
	cmd.Flag("receive.hashrings", "哈希环配置内容的替代文件标志位(优先级较低)").PlaceHolder("<content>").StringVar(&rc.hashringsFileContent)

	// 注册哈希环文件刷新间隔标志位
	rc.refreshInterval = extkingpin.ModelDuration(cmd.Flag("receive.hashrings-file-refresh-interval", "重新读取哈希环配置文件的刷新间隔(用作回退)").
		Default("5m"))

	// 注册本地接收节点端点标志位
	cmd.Flag("receive.local-endpoint", "本地接收节点的端点。用于在哈希环配置中标识本地节点").StringVar(&rc.endpoint)

	// 注册租户HTTP头标志位
	cmd.Flag("receive.tenant-header", "确定写入请求的租户的HTTP头").Default(receive.DefaultTenantHeader).StringVar(&rc.tenantHeader)

	// 注册默认租户ID标志位
	cmd.Flag("receive.default-tenant-id", "当没有通过头提供租户时使用的默认租户ID").Default(receive.DefaultTenant).StringVar(&rc.defaultTenantID)

	// 注册租户标签名称标志位
	cmd.Flag("receive.tenant-label-name", "租户将通过其声明的标签名称").Default(receive.DefaultTenantLabel).StringVar(&rc.tenantLabelName)

	// 注册副本HTTP头标志位
	cmd.Flag("receive.replica-header", "指定写入请求的副本号的HTTP头").Default(receive.DefaultReplicaHeader).StringVar(&rc.replicaHeader)

	// 注册复制因子标志位
	cmd.Flag("receive.replication-factor", "要复制多少次传入的写入请求").Default("1").Uint64Var(&rc.replicationFactor)

	// 注册转发请求超时标志位
	rc.forwardTimeout = extkingpin.ModelDuration(cmd.Flag("receive-forward-timeout", "每个转发请求的超时时间").Default("5s").Hidden())

	// 注册TSDB最小块持续时间标志位
	rc.tsdbMinBlockDuration = extkingpin.ModelDuration(cmd.Flag("tsdb.min-block-duration", "本地TSDB块的最小持续时间").Default("2h").Hidden())

	// 注册TSDB最大块持续时间标志位
	rc.tsdbMaxBlockDuration = extkingpin.ModelDuration(cmd.Flag("tsdb.max-block-duration", "本地TSDB块的最大持续时间").Default("2h").Hidden())

	// 注册TSDB允许重叠块标志位
	cmd.Flag("tsdb.allow-overlapping-blocks", "允许重叠块,从而启用垂直压缩和垂直查询合并").Default("false").BoolVar(&rc.tsdbAllowOverlappingBlocks)

	// 注册TSDB WAL压缩标志位
	cmd.Flag("tsdb.wal-compression", "压缩TSDB WAL").Default("true").BoolVar(&rc.walCompression)

	// 注册TSDB无锁文件标志位
	cmd.Flag("tsdb.no-lockfile", "不在TSDB数据目录中创建锁文件。在任何情况下,锁文件都将在下次启动时删除").Default("false").BoolVar(&rc.noLockFile)

	// 注册TSDB最大示例标志位
	cmd.Flag("tsdb.max-exemplars",
		"启用对示例的支持并设置每个租户将存储的最大示例数。如果示例存储已满(存储的示例数等于max-exemplars),则摄入新示例将从存储中逐出最旧的示例。此标志的0(或更小)值禁用示例存储").
		Default("0").Int64Var(&rc.tsdbMaxExemplars)

	// 注册哈希函数标志位
	cmd.Flag("hash-func", "指定在计算生成文件的哈希值时使用的哈希函数。如果没有指定函数,则不会发生。这允许避免两次下载某些文件,尽管会有一些性能开销。可能的值为: \"\", \"SHA256\"。").
		Default("").EnumVar(&rc.hashFunc, "SHA256", "")

	// 注册忽略不等块大小标志位
	cmd.Flag("shipper.ignore-unequal-block-size", "如果为true,则接收不需要将最小和最大块大小标志位设置为相同的值。仅当您希望保持较长的保留时间和压缩启用时,才使用此选项,因为在最坏的情况下,它可能导致Thanos存储桶存储中约2小时的数据丢失。").Default("false").Hidden().BoolVar(&rc.ignoreBlockSize)

	// 注册允许无序上传标志位
	cmd.Flag("shipper.allow-out-of-order-uploads",
		"如果为true,则发货方将跳过给定迭代中失败的块上传并在以后重试。这意味着一些较新的块可能比旧块更早地上传。这可能会触发没有这些块的压缩,并因此导致重叠情况。如果启用了垂直压缩并且希望尽快上传块而不关心顺序,请将其设置为true。").
		Default("false").Hidden().BoolVar(&rc.allowOutOfOrderUpload)

	// 注册请求日志配置标志位
	rc.reqLogConfig = extkingpin.RegisterRequestLoggingFlags(cmd)
}

// determineMode returns the ReceiverMode that this receiver is configured to run in.
// This is used to configure this Receiver's forwarding and ingesting behavior at runtime.
// determineMode 函数用于确定接收器的模式
//
// 参数:
//
//	rc *receiveConfig: 指向receiveConfig结构体的指针,包含接收配置信息
//
// 返回值:
//
//	receive.ReceiverMode: 返回接收器的模式,可以是RouterIngestor、RouterOnly或IngestorOnly
func (rc *receiveConfig) determineMode() receive.ReceiverMode {
	// 用户是否提供了某种哈希环配置?
	// Has the user provided some kind of hashring configuration?
	hashringSpecified := rc.hashringsFileContent != "" || rc.hashringsFilePath != ""
	// 用户是否指定了 --receive.local-endpoint 标志?
	// Has the user specified the --receive.local-endpoint flag?
	localEndpointSpecified := rc.endpoint != ""

	switch {
	case hashringSpecified && localEndpointSpecified:
		// 如果用户提供了哈希环配置并指定了本地端点,则返回 RouterIngestor
		// If the user has provided a hashring configuration and specified a local endpoint, return RouterIngestor
		return receive.RouterIngestor
	case hashringSpecified && !localEndpointSpecified:
		// 注意:如果哈希环包含指向自身的地址且未指定本地端点,则会创建无限循环/分叉炸弹
		// Be careful - if the hashring contains an address that routes to itself and does not specify a local
		// endpoint - you've just created an infinite loop / fork bomb :)
		// 如果用户提供了哈希环配置但未指定本地端点,则返回 RouterOnly
		// If the user has provided a hashring configuration but not a local endpoint, return RouterOnly
		return receive.RouterOnly
	default:
		// 未提供哈希环配置,因此我们会在本地接收所有指标
		// hashring configuration has not been provided so we ingest all metrics locally.
		// 如果用户未提供哈希环配置,则返回 IngestorOnly
		// If the user has not provided a hashring configuration, return IngestorOnly
		return receive.IngestorOnly
	}
}
posted @ 2025-02-12 20:26  左扬  阅读(26)  评论(0)    收藏  举报