Polaris系列-06.启动分析五

接上篇:开始 初始化服务发现模块相关功能里面的 健康检查初始化
在这里插入图片描述
点进去:代码省略了错误判断逻辑

func initialize(ctx context.Context, hcOpt *Config, bc *batch.Controller) error {
	// 设置健康检查的默认值:看下面数据模型:
	// 除了LocalHost和最后两项(前面已经初始化),都在这里面初始化值了
	hcOpt.SetDefault()
	storage, _ := store.GetStore()
	
	// 创建服务:带有一堆options参数配置:
	svr, _ := NewHealthServer(ctx, hcOpt,
		WithPlugins(),
		WithStore(storage),
		WithBatchController(bc),
		WithTimeAdjuster(newTimeAdjuster(ctx, storage)),
	)
	_server = svr

	// 服务run起来! 
	return svr.run(ctx)
}

// Config 健康检查配置
type Config struct {
	Open                *bool                  `yaml:"open"`
	Service             string                 `yaml:"service"`
	SlotNum             int                    `yaml:"slotNum"`
	LocalHost           string                 `yaml:"localHost"`
	MinCheckInterval    time.Duration          `yaml:"minCheckInterval"`
	MaxCheckInterval    time.Duration          `yaml:"maxCheckInterval"`
	ClientCheckInterval time.Duration          `yaml:"clientCheckInterval"`
	ClientCheckTtl      time.Duration          `yaml:"clientCheckTtl"`
	Checkers            []plugin.ConfigEntry   `yaml:"checkers"`
	Batch               map[string]interface{} `yaml:"batch"`
}

我们先看看NewHealthServer(...)的流程,再回头分析options参数部分
FYI: 省略了部分旁支代码:

func NewHealthServer(ctx context.Context, hcOpt *Config, options ...serverOption) (*Server, error) {
	
	options = append(options,
		withChecker(),
		withCacheProvider(),
		withCheckScheduler(newCheckScheduler(ctx, hcOpt.SlotNum, hcOpt.MinCheckInterval,
			hcOpt.MaxCheckInterval, hcOpt.ClientCheckInterval, hcOpt.ClientCheckTtl)),
		withDispatcher(ctx),
		// 这个必须保证在最后一个 option
		withSubscriber(ctx),
	)

	svr := &Server{
		hcOpt:     hcOpt,
		localHost: hcOpt.LocalHost,
	}
	// 给svr配置各种不同的参数 去初始化
	for i := range options {
		if err := options[i](svr); err != nil {
			return nil, err
		}
	}
	return svr, nil
}

// Server health checks the main server
// 大部分属性值通过options函数初始化:下面数字表示是通过第几个option函数初始化
type Server struct {
	hcOpt          *Config //这个直接初始化
	storage        store.Store	// 2
	defaultChecker plugin.HealthChecker		// 5
	checkers       map[int32]plugin.HealthChecker	// 5
	cacheProvider  *CacheProvider	// 6
	timeAdjuster   *TimeAdjuster	// 4
	dispatcher     *Dispatcher	// 8
	checkScheduler *CheckScheduler	// 7 : 它和本server互相引用
	history        plugin.History	// 1
	discoverEvent  plugin.DiscoverChannel  // 1
	localHost      string //这个直接初始化
	bc             *batch.Controller	// 3
	serviceCache   cachetypes.ServiceCache
	instanceCache  cachetypes.InstanceCache

	subCtxs []*eventhub.SubscribtionContext	// 9
}

所以我们看看上面那些options函数是如何参与healthServer的属性初始化的:
先看第一个:第120行:
在这里插入图片描述
svrhistorydiscoverEvent初始化值:

// WithPlugins .
func WithPlugins() serverOption {
	return func(svr *Server) error {
		svr.history = plugin.GetHistory()
		svr.discoverEvent = plugin.GetDiscoverEvent()
		return nil
	}
}

都是这个套路:

// WithStore .
func WithStore(s store.Store) serverOption {
	return func(svr *Server) error {
		svr.storage = s
		return nil
	}
}

。。 。。。
最后一个option:withSubscriber(...)我们进去看看:
FYI: 省略err判断逻辑

func withSubscriber(ctx context.Context) serverOption {
	return func(svr *Server) error {
		svr.subCtxs = make([]*eventhub.SubscribtionContext, 0, 4)

		subCtx, _ := eventhub.SubscribeWithFunc(eventhub.CacheInstanceEventTopic,
			svr.cacheProvider.handleInstanceCacheEvent)
		
		svr.subCtxs = append(svr.subCtxs, subCtx)
		subCtx, _ = eventhub.SubscribeWithFunc(eventhub.CacheClientEventTopic,
			svr.cacheProvider.handleClientCacheEvent)
		
		svr.subCtxs = append(svr.subCtxs, subCtx)

		leaderChangeEventHandler := newLeaderChangeEventHandler(svr)
		subCtx, _ = eventhub.Subscribe(eventhub.LeaderChangeEventTopic, leaderChangeEventHandler)
		
		svr.subCtxs = append(svr.subCtxs, subCtx)

		resourceEventHandler := newResourceHealthCheckHandler(ctx, svr)
		// 监听服务实例的删除事件,然后清理心跳 key 数据
		subCtx, _ = eventhub.Subscribe(eventhub.InstanceEventTopic, resourceEventHandler)
		
		svr.subCtxs = append(svr.subCtxs, subCtx)

		// 监听客户端实例的删除事件,然后清理心跳 key 数据
		subCtx, _ = eventhub.Subscribe(eventhub.ClientEventTopic, resourceEventHandler)
		
		svr.subCtxs = append(svr.subCtxs, subCtx)

		if err := svr.storage.StartLeaderElection(store.ElectionKeySelfServiceChecker); err != nil {
			return err
		}
		return nil
	}
}

总结就是:给svrsubCtxs属性赋值。我们看看其中的SubscribeWithFunc
省略err判断:

// SubscribeWithFunc subscribe topic use func
func SubscribeWithFunc(topic string, handler HandlerFunc, opts ...SubOption) (*SubscribtionContext, error) {
	
	return globalEventHub.Subscribe(topic, &funcSubscriber{
		handlerFunc: handler,
	}, opts...)
}

func (e *eventHub) Subscribe(topic string, handler Handler,
	opts ...SubOption) (*SubscribtionContext, error) {
	// 1.如果有,直接返回,否则 去创建
	t := e.loadOrStoreTopic(topic)
	// 2.订阅
	return t.subscribe(e.ctx, handler, opts...)
}

先看1:

// 省略读取逻辑:
func (e *eventHub) createTopic(name string, _ PublishOption) *topic {
	t := newTopic(name)
	e.topics[name] = t
	// 运行监听:读取队列消息
	go t.run(e.ctx)
	return t
}

// eventHub event hub
type eventHub struct {
	ctx    context.Context
	cancel context.CancelFunc
	topics map[string]*topic
	mu     sync.RWMutex
}

看看创建出topic后启动监听逻辑:从topic的队列中读取消息,然后 推送给 它所有的订阅者们

// run read msg from topic queue and send to all subscription
func (t *topic) run(ctx context.Context) {
	log.Infof("[EventHub] topic:%s run dispatch", t.name)
	for {
		select {
		case msg := <-t.queue:
			func() {
				// 加锁获取订阅者们
				subs := t.listSubscribers()
				for i := range subs {
					sub := subs[i]
					// 给sub推送消息数据
					go sub.send(ctx, msg)
				}
			}()
			// 监听退出信号...
		}
	}
}

type topic struct {
	name    string
	queue   chan Event // 消息事件队列
	closeCh chan struct{}  // 退出信号
	subs    map[string]*subscription // 订阅者们
	mu      sync.RWMutex
}

再看如何给订阅者推送消息数据的:只是把消息事件推送到订阅者的队列中
代码省略了退出监听逻辑 和 日志打印

func (s *subscription) send(ctx context.Context, event Event) {
	select {
	case s.queue <- event:
	}
}

// Subscription subscription info
type subscription struct {
	name    string
	queue   chan Event // 队列
	closeCh chan struct{}
	handler Handler // 处理函数
	opts    *SubOptions
}

到这步,我们梳理了创建topic, 启动一个协程,监听读取到topic队列消息后,往订阅者们的队列中推送…
但是谁订阅这个topic呢?,接着往下看2:

return t.subscribe(e.ctx, handler, opts...)
// subscribe subscribe msg from topic
func (t *topic) subscribe(ctx context.Context, handler Handler,
	opts ...SubOption) (*SubscribtionContext, error) {

	subID := uuid.NewString()
	// 创建订阅者
	sub := newSubscription(subID, handler, opts...)

	t.mu.Lock()
	defer t.mu.Unlock()
	// 订阅者sub 订阅了主题:t
	t.subs[subID] = sub

	newCtx, cancel := context.WithCancel(ctx)
	subscribtionCtx := &SubscribtionContext{
		subID: subID,
		cancel: func() {
			cancel()
			// 给订阅者留了一个取消订阅函数
			t.unsubscribe(subID)
		},
	}
	// 订阅者开始启动协程 接收 来自topic推送 的消息数据
	go sub.receive(newCtx)
	
	return subscribtionCtx, nil
}

最后看看sub.receive(ctx):
核心处理逻辑如下:

func (s *subscription) receive(ctx context.Context) {
	for {
		select {
		// 从自身的队列中取出数据:
		case event := <-s.queue:
			// 先 预处理
			event = s.handler.PreProcess(ctx, event)
			// 正式处理
			if err := s.handler.OnEvent(ctx, event); err != nil {
				log.Errorf("[EventHub] subscriptions:%s handler event error:%s", s.name, err.Error())
			}
		}
	}
}

再总结图之前,再看看订阅者如何处理它队列中的消息呢?

handler处理逻辑:创建/更新/删除服务缓存健康数据,
维护在下面自己的数据模型中

func (c *CacheProvider) handleInstanceCacheEvent(ctx context.Context, args interface{}) error {
	event, ok := args.(*eventhub.CacheInstanceEvent)
	if !ok {
		return nil
	}
	switch event.EventType {
	case eventhub.EventCreated:
		c.OnCreated(event.Instance)
	case eventhub.EventUpdated:
		c.OnUpdated(event.Instance)
	case eventhub.EventDeleted:
		c.OnDeleted(event.Instance)
	}
	return nil
}

// CacheProvider provider health check objects for service cache
type CacheProvider struct {
	svr                  *Server
	selfService          string
	selfServiceInstances *utils.SegmentMap[string, ItemWithChecker]
	healthCheckInstances *utils.SegmentMap[string, ItemWithChecker]
	healthCheckClients   *utils.SegmentMap[string, ItemWithChecker]
}

先抛开具体处理逻辑不谈,我们总结一张流程图:
在这里插入图片描述
构建出healthcheck.Server后,再看它如何run起来:

func (s *Server) run(ctx context.Context) error {
	// 所以我们是可以控制不要健康检查的
	if !s.isOpen() {
		return nil
	}
	s.checkScheduler.run(ctx)
	s.timeAdjuster.doTimeAdjust(ctx)
	s.dispatcher.startDispatchingJob(ctx)
	return nil
}

func (c *CheckScheduler) run(ctx context.Context) {
	go c.doCheckInstances(ctx)
	go c.doCheckClient(ctx)
	go c.doAdopt(ctx)
}

里面用到时间轮设计,time.Ticker定时器:定时去做各种各样的check操作。。。

至此 健康检查初步介绍完毕。往下:
在这里插入图片描述

type InitOption func(s *Server)

service.WithBatchController(bc),
service.WithStorage(s),
service.WithCacheManager(&cfg.Cache, cacheMgn),
service.WithHealthCheckSvr(healthCheckServer),
service.WithNamespaceSvr(namespaceSvr),

和上述健康检查服务 配置处理逻辑一样,不再赘述,
只是WithCacheManager又初始化了一些缓存资源

func GetChainOrder() []string {
	return []string{
		"auth",
		"paramcheck",
	}
}

初始化服务模块逻辑
1.插件初始化:里面又有事件订阅
2.拦截器模式:返回包装代理的 DiscoverServer(包装了auth + paramcheck)
在这里插入图片描述至此StartDiscoverComponents初步介绍完毕,下一篇从
StartConfigCenterComponents初始化配置中心模块相关功能开始。

在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值