rootcoord启动源码分析

rootcoord启动源码分析

结构体

// Server grpc wrapper
type Server struct {
	rootCoord   types.RootCoordComponent
	grpcServer  *grpc.Server
	grpcErrChan chan error

	wg sync.WaitGroup

	ctx    context.Context
	cancel context.CancelFunc

	serverID atomic.Int64

	etcdCli    *clientv3.Client
	tikvCli    *txnkv.Client
	dataCoord  types.DataCoordClient
	queryCoord types.QueryCoordClient

	newDataCoordClient  func(string, *clientv3.Client) types.DataCoordClient
	newQueryCoordClient func(string, *clientv3.Client) types.QueryCoordClient
}

分析变量rootCoord、dataCoord、queryCoord是何时赋予的值。

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

func (mr *MilvusRoles) runRootCoord(ctx context.Context, localMsg bool, wg *sync.WaitGroup) component {
	wg.Add(1)
	return runComponent(ctx, localMsg, wg, components.NewRootCoord, metrics.RegisterRootCoord)
}

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

components.NewRootCoord是一个函数。

NewRootCoord()用来创建RootCoord结构体。

// NewRootCoord creates a new RoorCoord
func NewRootCoord(ctx context.Context, factory dependency.Factory) (*RootCoord, error) {
	svr, err := rc.NewServer(ctx, factory)
	if err != nil {
		return nil, err
	}
	return &RootCoord{
		ctx: ctx,
		svr: svr,
	}, nil
}

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

// NewServer create a new RootCoord grpc server.
func NewServer(ctx context.Context, factory dependency.Factory) (*Server, error) {
	ctx1, cancel := context.WithCancel(ctx)
	s := &Server{
		ctx:         ctx1,
		cancel:      cancel,
		grpcErrChan: make(chan error),
	}
    // 定义s.newDataCoordClient和s.newQueryCoordClient函数的具体实现代码
	s.setClient()
	var err error
    // 产生的值是types.RootCoordComponent接口的一个实现
	s.rootCoord, err = rootcoord.NewCore(s.ctx, factory)
	if err != nil {
		return nil, err
	}
	return s, err
}

rootcoord.NewCore()产生结构体,是types.RootCoordComponent接口的实现。

s.rootCoord的结构体叫做Core。说明rootcoord是核心。

代码位于internal\rootcoord\root_coord.go

执行Run()

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

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()方法,这里是RootCoord结构体
		if err := role.Run(); err != nil {
			panic(err)
		}
		runWg.Done()
	}()
    ......
}

runComponent是一个包裹函数。

// Run starts service
func (rc *RootCoord) Run() error {
	if err := rc.svr.Run(); err != nil {
		log.Error("RootCoord starts error", zap.Error(err))
		return err
	}
	log.Info("RootCoord successfully started")
	return nil
}

Run()方法调用rc.svr.Run()方法。srv是rc.NewServer()返回的结构体。

// Run initializes and starts RootCoord's grpc service.
func (s *Server) Run() error {
	if err := s.init(); err != nil {
		return err
	}
	log.Debug("RootCoord init done ...")

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

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

s.init()

func (s *Server) init() error {
	params := paramtable.Get()
	etcdConfig := &params.EtcdCfg
	rpcParams := &params.RootCoordGrpcServerCfg
	log.Debug("init params done..")

	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("RootCoord connect to etcd failed", zap.Error(err))
		return err
	}
	s.etcdCli = etcdCli
	s.rootCoord.SetEtcdClient(s.etcdCli)
	s.rootCoord.SetAddress(rpcParams.GetAddress())
	log.Debug("etcd connect done ...")

	if params.MetaStoreCfg.MetaStoreType.GetValue() == util.MetaStoreTypeTiKV {
		log.Info("Connecting to tikv metadata storage.")
		s.tikvCli, err = getTiKVClient(&paramtable.Get().TiKVCfg)
		if err != nil {
			log.Debug("RootCoord failed to connect to tikv", zap.Error(err))
			return err
		}
		s.rootCoord.SetTiKVClient(s.tikvCli)
		log.Info("Connected to tikv. Using tikv as metadata storage.")
	}
    // 启动grpc服务,开启服务端口
	err = s.startGrpc(rpcParams.Port.GetAsInt())
	if err != nil {
		return err
	}
	log.Debug("grpc init done ...")
    // s.setClient()已经设置s.newDataCoordClient
	if s.newDataCoordClient != nil {
		log.Debug("RootCoord start to create DataCoord client")
		dataCoord := s.newDataCoordClient(rootcoord.Params.EtcdCfg.MetaRootPath.GetValue(), s.etcdCli)
		s.dataCoord = dataCoord
		if err := s.rootCoord.SetDataCoordClient(dataCoord); err != nil {
			panic(err)
		}
	}
    // s.setClient()已经设置s.newQueryCoordClient
	if s.newQueryCoordClient != nil {
		log.Debug("RootCoord start to create QueryCoord client")
		queryCoord := s.newQueryCoordClient(rootcoord.Params.EtcdCfg.MetaRootPath.GetValue(), s.etcdCli)
		s.queryCoord = queryCoord
		if err := s.rootCoord.SetQueryCoordClient(queryCoord); err != nil {
			panic(err)
		}
	}

	return s.rootCoord.Init()
}

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

s.dataCoord通过s.newDataCoordClient设置。

s.queryCoord通过s.newQueryCoordClient设置。

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

最终调用s.rootCoord.Init()进行初始化,代码位置:internal\rootcoord\root_coord.go

s.rootCoord接口types.RootCoordComponent,RootCoordComponent继承于Component。

type RootCoord interface {
	Component
	rootcoordpb.RootCoordServer
}

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

接口套接口:

RootCoordComponent -> RootCoord -> Component
DataCoordComponent -> DataCoord -> Component
QueryCoordComponent -> QueryCoord -> Component
ProxyComponent -> Proxy -> Component
QueryNodeComponent -> QueryNode -> Component
IndexNodeComponent -> IndexNode -> Component
DataNodeComponent -> DataNode -> Component

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

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()

回过头来继续rootcoord的init。

// Init initialize routine
func (c *Core) Init() error {
	var initError error
	c.factory.Init(Params)
	if err := c.initSession(); err != nil {
		return err
	}

	if c.enableActiveStandBy {
		......
	} else {
		c.initOnce.Do(func() {
		    // 真正执行初始化的函数
			initError = c.initInternal()
		})
	}

	return initError
}

继续进入c.initInternal():

func (c *Core) initInternal() error {
	c.UpdateStateCode(commonpb.StateCode_Initializing)
	c.initKVCreator()

	if err := c.initIDAllocator(); err != nil {
		return err
	}

	if err := c.initTSOAllocator(); err != nil {
		return err
	}

	if err := c.initMetaTable(); err != nil {
		return err
	}

	c.scheduler = newScheduler(c.ctx, c.idAllocator, c.tsoAllocator)

	c.factory.Init(Params)
	chanMap := c.meta.ListCollectionPhysicalChannels()
	c.chanTimeTick = newTimeTickSync(c.ctx, c.session.ServerID, c.factory, chanMap)
	c.proxyClientManager = newProxyClientManager(c.proxyCreator)

	c.broker = newServerBroker(c)
	c.ddlTsLockManager = newDdlTsLockManager(c.tsoAllocator)
	c.garbageCollector = newBgGarbageCollector(c)
	c.stepExecutor = newBgStepExecutor(c.ctx)

	c.proxyManager = newProxyManager(
		c.ctx,
		c.etcdCli,
		c.chanTimeTick.initSessions,
		c.proxyClientManager.GetProxyClients,
	)
	c.proxyManager.AddSessionFunc(c.chanTimeTick.addSession, c.proxyClientManager.AddProxyClient)
	c.proxyManager.DelSessionFunc(c.chanTimeTick.delSession, c.proxyClientManager.DelProxyClient)

	c.metricsCacheManager = metricsinfo.NewMetricsCacheManager()

	c.quotaCenter = NewQuotaCenter(c.proxyClientManager, c.queryCoord, c.dataCoord, c.tsoAllocator, c.meta)
	log.Debug("RootCoord init QuotaCenter done")

	if err := c.initImportManager(); err != nil {
		return err
	}

	if err := c.initCredentials(); err != nil {
		return err
	}

	if err := c.initRbac(); err != nil {
		return err
	}

	return nil
}

从代码可以看出初始化是在填充Core结构体的变量。

s.start()

启动组件的逻辑。

func (s *Server) start() error {
	log.Info("RootCoord Core start ...")
	if err := s.rootCoord.Register(); err != nil {
		log.Error("RootCoord registers service failed", zap.Error(err))
		return err
	}

	if err := s.rootCoord.Start(); err != nil {
		log.Error("RootCoord start service failed", zap.Error(err))
		return err
	}

	return nil
}

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

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

Start():用来启动组件。

// Start starts RootCoord.
func (c *Core) Start() error {
	var err error
	if !c.enableActiveStandBy {
		c.startOnce.Do(func() {
			err = c.startInternal()
		})
	}

	return err
}

真正执行启动逻辑在c.startInternal()。

func (c *Core) startInternal() error {
	if err := c.proxyManager.WatchProxy(); err != nil {
		log.Fatal("rootcoord failed to watch proxy", zap.Error(err))
		// you can not just stuck here,
		panic(err)
	}

	if err := c.restore(c.ctx); err != nil {
		panic(err)
	}

	if Params.QuotaConfig.QuotaAndLimitsEnabled.GetAsBool() {
		go c.quotaCenter.run()
	}

	c.scheduler.Start()
	c.stepExecutor.Start()
	go func() {
		// refresh rbac cache
		if err := retry.Do(c.ctx, func() error {
			if err := c.proxyClientManager.RefreshPolicyInfoCache(c.ctx, &proxypb.RefreshPolicyInfoCacheRequest{
				OpType: int32(typeutil.CacheRefresh),
			}); err != nil {
				log.Info("fail to refresh policy info cache", zap.Error(err))
				return err
			}
			return nil
		}, retry.Attempts(100), retry.Sleep(time.Second)); err != nil {
			log.Warn("fail to refresh policy info cache", zap.Error(err))
		}
	}()

	c.startServerLoop()
	c.UpdateStateCode(commonpb.StateCode_Healthy)
	sessionutil.SaveServerInfo(typeutil.RootCoordRole, c.session.ServerID)
	logutil.Logger(c.ctx).Info("rootcoord startup successfully")

	return nil
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shulu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值