接上篇:开始 初始化服务发现模块相关功能
里面的 健康检查初始化
:
点进去:代码省略了错误判断逻辑
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行:
给svr
的history
和discoverEvent
初始化值:
// 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
}
}
总结就是:给svr
的subCtxs
属性赋值。我们看看其中的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
初始化配置中心模块相关功能开始。