github.com/polarismesh/polaris@v1.17.8/config/watcher.go (about)

     1  /**
     2   * Tencent is pleased to support the open source community by making Polaris available.
     3   *
     4   * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
     5   *
     6   * Licensed under the BSD 3-Clause License (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   * https://opensource.org/licenses/BSD-3-Clause
    11   *
    12   * Unless required by applicable law or agreed to in writing, software distributed
    13   * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    14   * CONDITIONS OF ANY KIND, either express or implied. See the License for the
    15   * specific language governing permissions and limitations under the License.
    16   */
    17  
    18  package config
    19  
    20  import (
    21  	"context"
    22  	"sync"
    23  
    24  	apiconfig "github.com/polarismesh/specification/source/go/api/v1/config_manage"
    25  	"go.uber.org/zap"
    26  
    27  	"github.com/polarismesh/polaris/common/eventhub"
    28  	"github.com/polarismesh/polaris/common/model"
    29  	"github.com/polarismesh/polaris/common/utils"
    30  )
    31  
    32  const (
    33  	QueueSize = 10240
    34  )
    35  
    36  type FileReleaseCallback func(clientId string, rsp *apiconfig.ConfigClientResponse) bool
    37  
    38  type watchContext struct {
    39  	watchConfigFiles []*apiconfig.ClientConfigFileInfo
    40  	clientId         string
    41  	fileReleaseCb    FileReleaseCallback
    42  	ClientVersion    uint64
    43  }
    44  
    45  // watchCenter 处理客户端订阅配置请求,监听配置文件发布事件通知客户端
    46  type watchCenter struct {
    47  	connManager *connManager
    48  	subCtx      *eventhub.SubscribtionContext
    49  	lock        sync.Mutex
    50  	// fileId -> clientId -> watchContext
    51  	configFileWatchers *utils.SyncMap[string, *utils.SyncMap[string, *watchContext]]
    52  }
    53  
    54  // NewWatchCenter 创建一个客户端监听配置发布的处理中心
    55  func NewWatchCenter() (*watchCenter, error) {
    56  	wc := &watchCenter{
    57  		configFileWatchers: utils.NewSyncMap[string, *utils.SyncMap[string, *watchContext]](),
    58  	}
    59  
    60  	var err error
    61  	wc.subCtx, err = eventhub.Subscribe(eventhub.ConfigFilePublishTopic, wc, eventhub.WithQueueSize(QueueSize))
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	return wc, nil
    66  }
    67  
    68  // PreProcess do preprocess logic for event
    69  func (wc *watchCenter) PreProcess(_ context.Context, e any) any {
    70  	return e
    71  }
    72  
    73  // OnEvent event process logic
    74  func (wc *watchCenter) OnEvent(ctx context.Context, arg any) error {
    75  	event, ok := arg.(*eventhub.PublishConfigFileEvent)
    76  	if !ok {
    77  		log.Warn("[Config][Watcher] receive invalid event type")
    78  		return nil
    79  	}
    80  	wc.notifyToWatchers(event.Message)
    81  	return nil
    82  }
    83  
    84  // AddWatcher 新增订阅者
    85  func (wc *watchCenter) AddWatcher(clientId string, watchConfigFiles []*apiconfig.ClientConfigFileInfo,
    86  	fileReleaseCb FileReleaseCallback) {
    87  	if len(watchConfigFiles) == 0 {
    88  		return
    89  	}
    90  	for _, file := range watchConfigFiles {
    91  		watchFileId := utils.GenFileId(file.Namespace.GetValue(), file.Group.GetValue(), file.FileName.GetValue())
    92  		log.Info("[Config][Watcher] add watcher.", zap.Any("client-id", clientId),
    93  			zap.String("watch-file-id", watchFileId), zap.Uint64("client-version", file.Version.GetValue()))
    94  
    95  		watchers, _ := wc.configFileWatchers.ComputeIfAbsent(watchFileId,
    96  			func(k string) *utils.SyncMap[string, *watchContext] {
    97  				newWatchers := utils.NewSyncMap[string, *watchContext]()
    98  				return newWatchers
    99  			})
   100  		watchers.Store(clientId, &watchContext{
   101  			clientId:         clientId,
   102  			fileReleaseCb:    fileReleaseCb,
   103  			ClientVersion:    file.Version.GetValue(),
   104  			watchConfigFiles: watchConfigFiles,
   105  		})
   106  	}
   107  }
   108  
   109  // RemoveWatcher 删除订阅者
   110  func (wc *watchCenter) RemoveWatcher(clientId string, watchConfigFiles []*apiconfig.ClientConfigFileInfo) {
   111  	if len(watchConfigFiles) == 0 {
   112  		return
   113  	}
   114  
   115  	for _, file := range watchConfigFiles {
   116  		watchFileId := utils.GenFileId(file.Namespace.GetValue(), file.Group.GetValue(), file.FileName.GetValue())
   117  		watchers, ok := wc.configFileWatchers.Load(watchFileId)
   118  		if !ok {
   119  			continue
   120  		}
   121  		watchers.Delete(clientId)
   122  	}
   123  }
   124  
   125  func (wc *watchCenter) notifyToWatchers(publishConfigFile *model.SimpleConfigFileRelease) {
   126  	watchFileId := utils.GenFileId(publishConfigFile.Namespace, publishConfigFile.Group, publishConfigFile.FileName)
   127  
   128  	log.Info("[Config][Watcher] received config file publish message.", zap.String("file", watchFileId))
   129  	watchers, ok := wc.configFileWatchers.Load(watchFileId)
   130  	if !ok {
   131  		return
   132  	}
   133  
   134  	response := GenConfigFileResponse(publishConfigFile.Namespace, publishConfigFile.Group,
   135  		publishConfigFile.FileName, "", publishConfigFile.Md5, publishConfigFile.Version)
   136  
   137  	waitNotifiers := watchers.Values()
   138  	for _, watchCtx := range waitNotifiers {
   139  		clientId := watchCtx.clientId
   140  		if watchCtx.ClientVersion < publishConfigFile.Version {
   141  			watchCtx.fileReleaseCb(clientId, response)
   142  			log.Info("[Config][Watcher] notify to client.",
   143  				zap.String("file", watchFileId), zap.String("clientId", clientId),
   144  				zap.Uint64("version", publishConfigFile.Version))
   145  		} else {
   146  			log.Info("[Config][Watcher] notify to client ignore.",
   147  				zap.String("file", watchFileId), zap.String("clientId", clientId),
   148  				zap.Uint64("client-version", watchCtx.ClientVersion),
   149  				zap.Uint64("version", publishConfigFile.Version))
   150  		}
   151  	}
   152  }