milvus datacoord启动源码分析

datacoord启动源码分析

结构体

go 复制代码
// components.DataCoord
// DataCoord implements grpc server of DataCoord server
type DataCoord struct {
	ctx context.Context
	svr *grpcdatacoordclient.Server
}

// grpcdatacoord.Server
// Server is the grpc server of datacoord
type Server structgrpcdatacoord.Context
	cancel context.CancelFunc

	serverID atomic.Int64

	wg        sync.WaitGroup
	dataCoord types.DataCoordComponent

	etcdCli *clientv3.Client
	tikvCli *txnkv.Client

	grpcErrChan chan error
	grpcServer  *grpc.Server
}

dataCoord是一个接口,实现dataCoord api功能。

go 复制代码
func (mr *MilvusRoles) runDataCoord(ctx context.Context, localMsg bool, wg *sync.WaitGroup) component {
	wg.Add(1)
	return runComponent(ctx, localMsg, wg, components.NewDataCoord, metrics.RegisterDataCoord)
}

// creator用NewDataCoord替换
role, err = creator(ctx, factory)

components.NewDataCoord是一个函数。

NewDataCoord()用来创建DataCoord结构体。

go 复制代码
// NewDataCoord creates a new DataCoord
func NewDataCoord(ctx context.Context, factory dependency.Factory) (*DataCoord, error) {
	s := grpcdatacoordclient.NewServer(ctx, factory)

	return &DataCoord{
		ctx: ctx,
		svr: s,
	}, nil
}

grpcdatacoordclient.NewServer()产生的是本结构体Server。

进入NewServer:

go 复制代码
// NewServer new data service grpc server
func NewServer(ctx context.Context, factory dependency.Factory, opts ...datacoord.Option) *Server {
	ctx1, cancel := context.WithCancel(ctx)

	s := &Server{
		ctx:         ctx1,
		cancel:      cancel,
		grpcErrChan: make(chan error),
	}
	s.dataCoord = datacoord.CreateServer(s.ctx, factory, opts...)
	return s
}

datacoord.CreateServer()返回一个结构体datacoord.Server,是接口types.DataCoordComponent的实现。

执行Run()

Server结构体创建后,调用结构体的Run()方法。

go 复制代码
func runComponent[T component](ctx context.Context,
	localMsg bool,
	runWg *sync.WaitGroup,
	creator func(context.Context, dependency.Factory) (T, error),
	metricRegister func(*prometheus.Registry),
) component {
	var role T

	sign := make(chan struct{})
	go func() {
		factory := dependency.NewFactory(localMsg)
		var err error
		role, err = creator(ctx, factory)
		if localMsg {
			paramtable.SetRole(typeutil.StandaloneRole)
		} else {
			paramtable.SetRole(role.GetName())
		}
		if err != nil {
			panic(err)
		}
		close(sign)
        // 在这里调用对应组件结构体的Run()方法,这里是components.DataCoord结构体
		if err := role.Run(); err != nil {
			panic(err)
		}
		runWg.Done()
	}()
    ......
}

runComponent是一个包裹函数。

go 复制代码
// Run starts service
func (s *DataCoord) Run() error {
	if err := s.svr.Run(); err != nil {
		log.Error("DataCoord starts error", zap.Error(err))
		return err
	}
	log.Debug("DataCoord successfully started")
	return nil
}

Run()方法调用s.svr.Run()方法。srv是datacoord.CreateServer()返回的结构体datacoord.Server。

go 复制代码
// grpcdatacoord
// Run starts the Server. Need to call inner init and start method.
func (s *Server) Run() error {
	if err := s.init(); err != nil {
		return err
	}
	log.Debug("DataCoord init done ...")

	if err := s.start(); err != nil {
		return err
	}
	log.Debug("DataCoord start done ...")
	return nil
}

接下来分析s.init()和s.start()方法。

s.init()

go 复制代码
func (s *Server) init() error {
	params := paramtable.Get()
	etcdConfig := &params.EtcdCfg

	etcdCli, err := etcd.GetEtcdClient(
		etcdConfig.UseEmbedEtcd.GetAsBool(),
		etcdConfig.EtcdUseSSL.GetAsBool(),
		etcdConfig.Endpoints.GetAsStrings(),
		etcdConfig.EtcdTLSCert.GetValue(),
		etcdConfig.EtcdTLSKey.GetValue(),
		etcdConfig.EtcdTLSCACert.GetValue(),
		etcdConfig.EtcdTLSMinVersion.GetValue())
	if err != nil {
		log.Debug("DataCoord connect to etcd failed", zap.Error(err))
		return err
	}
	s.etcdCli = etcdCli
	s.dataCoord.SetEtcdClient(etcdCli)
	s.dataCoord.SetAddress(params.DataCoordGrpcServerCfg.GetAddress())

	if params.MetaStoreCfg.MetaStoreType.GetValue() == util.MetaStoreTypeTiKV {
		log.Info("Connecting to tikv metadata storage.")
		tikvCli, err := getTiKVClient(&paramtable.Get().TiKVCfg)
		if err != nil {
			log.Warn("DataCoord failed to connect to tikv", zap.Error(err))
			return err
		}
		s.dataCoord.SetTiKVClient(tikvCli)
		log.Info("Connected to tikv. Using tikv as metadata storage.")
	}
    // 启动grpc,默认为13333
	err = s.startGrpc()
	if err != nil {
		log.Debug("DataCoord startGrpc failed", zap.Error(err))
		return err
	}
    // 执行真正的初始化
	if err := s.dataCoord.Init(); err != nil {
		log.Error("dataCoord init error", zap.Error(err))
		return err
	}
	return nil
}

这段可以看出来,创建了etcdCli并赋予给了s.etcdCli。

s.startGrpc()启动grpc端口服务。

最终调用s.dataCoord.Init()进行初始化,代码位置:internal\datacoord\server.go

s.queryCoord是接口类型types.DataCoordComponent,DataCoordComponent继承于Component。

go 复制代码
type DataCoordComponent interface {
    DataCoord
    SetAddress(address string)
    SetEtcdClient(etcdClient *clientv3.Client)
    SetTiKVClient(client *txnkv.Client)
    SetRootCoordClient(rootCoord RootCoordClient)
    SetDataNodeCreator(func(context.Context, string, int64) (DataNodeClient, error))
    SetIndexNodeCreator(func(context.Context, string, int64) (IndexNodeClient, error))
}

// DataCoord is the interface `datacoord` package implements
type DataCoord interface {
	Component
	datapb.DataCoordServer
}

// Component is the interface all services implement
type Component interface {
	Init() error
	Start() error
	Stop() error
	Register() error
}

接口套接口:

shell 复制代码
RootCoordComponent -> RootCoord -> Component
DataCoordComponent -> DataCoord -> Component
QueryCoordComponent -> QueryCoord -> Component
ProxyComponent -> Proxy -> Component
QueryNodeComponent -> QueryNode -> Component
IndexNodeComponent -> IndexNode -> Component
DataNodeComponent -> DataNode -> Component

各组件最终的Init()初始化代码路径:

shell 复制代码
internal\rootcoord\root_coord.go->Init()
internal\datacoord\server.go->Init()
internal\querycoordv2\server.go->Init()
internal\datanode\data_node.go->Init()
internal\indexnode\indexnode.go->Init()
internal\querynodev2\server.go->Init()
internal\proxy\proxy.go->Init()

回过头来继续datacoord的init。

go 复制代码
// Init change server state to Initializing
func (s *Server) Init() error {
	var err error
	s.factory.Init(Params)
	if err = s.initSession(); err != nil {
		return err
	}
	if s.enableActiveStandBy {
		......
	}
    // 执行真正的初始化
	return s.initDataCoord()
}

继续进入c.initDataCoord():

go 复制代码
func (s *Server) initDataCoord() error {
	s.stateCode.Store(commonpb.StateCode_Initializing)
	var err error
	if err = s.initRootCoordClient(); err != nil {
		return err
	}

	s.broker = NewCoordinatorBroker(s.rootCoordClient)

	storageCli, err := s.newChunkManagerFactory()
	if err != nil {
		return err
	}

	if err = s.initMeta(storageCli); err != nil {
		return err
	}

	s.handler = newServerHandler(s)

	if err = s.initCluster(); err != nil {
		return err
	}

	s.allocator = newRootCoordAllocator(s.rootCoordClient)

	s.initIndexNodeManager()

	if err = s.initServiceDiscovery(); err != nil {
		return err
	}

	if Params.DataCoordCfg.EnableCompaction.GetAsBool() {
		s.createCompactionHandler()
		s.createCompactionTrigger()
	}

	if err = s.initSegmentManager(); err != nil {
		return err
	}

	s.initGarbageCollection(storageCli)
	s.initIndexBuilder(storageCli)

	s.serverLoopCtx, s.serverLoopCancel = context.WithCancel(s.ctx)

	return nil
}

从代码可以看出初始化是在填充datacoord结构体。

s.start()

启动组件的逻辑。

go 复制代码
func (s *Server) start() error {
	err := s.dataCoord.Register()
	if err != nil {
		log.Debug("DataCoord register service failed", zap.Error(err))
		return err
	}

	err = s.dataCoord.Start()
	if err != nil {
		log.Error("DataCoord start failed", zap.Error(err))
		return err
	}
	return nil
}

s.dataCoord是一个Component接口,实现了 方法Init()、 Start() 、 Stop() 、 Register() 。

Register():向元数据etcd注册。

Start():用来启动组件。

进入s.dataCoord.Start():

go 复制代码
func (s *Server) Start() error {
	if !s.enableActiveStandBy {
		s.startDataCoord()
		log.Info("DataCoord startup successfully")
	}

	return nil
}

真正执行启动逻辑在s.startDataCoord()。

go 复制代码
func (s *Server) startDataCoord() {
	if Params.DataCoordCfg.EnableCompaction.GetAsBool() {
		s.compactionHandler.start()
		s.compactionTrigger.start()
	}
	s.startServerLoop()
	s.stateCode.Store(commonpb.StateCode_Healthy)
	sessionutil.SaveServerInfo(typeutil.DataCoordRole, s.session.ServerID)
}

要详细知道启动querycoord组件做了什么事情,研究这个函数。

相关推荐
AC赳赳老秦13 小时前
Dify工作流+DeepSeek:运维自动化闭环(数据采集→报告生成)
android·大数据·运维·数据库·人工智能·golang·deepseek
源代码•宸14 小时前
Leetcode—3. 无重复字符的最长子串【中等】
经验分享·后端·算法·leetcode·面试·golang·string
storyseek14 小时前
关于Milvus向量数据库的基础
数据库·milvus
阳明Coding15 小时前
golang从入门到通天—数据库操作(gorm框架使用)(最简单最详细的golang学习笔记)
笔记·学习·golang
源代码•宸19 小时前
Golang原理剖析(逃逸分析)
经验分享·后端·算法·面试·golang··内存逃逸
moxiaoran575320 小时前
Go语言并发处理
开发语言·后端·golang
Tony Bai20 小时前
AI 时代,Go 语言会“失宠”还是“封神”?—— GopherCon 2025 圆桌深度复盘
开发语言·人工智能·后端·golang
WeiAreYoung20 小时前
uni-app xcode 制作iOS Notification Service Extension 远程推送图文原生插件
ios·uni-app·xcode
tc&20 小时前
新虚拟机安装 Go 环境:问题总结与解决方案
开发语言·后端·golang
钟离墨笺1 天前
Go语言--2go基础-->基本数据类型
开发语言·前端·后端·golang