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 := ¶ms.EtcdCfg
rpcParams := ¶ms.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(¶mtable.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组件做了什么事情,研究这个函数。