github.com/zly-app/zapp@v1.3.3/plugin/apollo_provider/provider.go (about)

     1  package apollo_provider
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  
     9  	"go.uber.org/zap"
    10  
    11  	"github.com/zly-app/zapp/config"
    12  	"github.com/zly-app/zapp/config/apollo_sdk"
    13  	"github.com/zly-app/zapp/core"
    14  	"github.com/zly-app/zapp/logger"
    15  )
    16  
    17  // 观察失败等待时间
    18  var WatchErrWaitTime = time.Second * 5
    19  
    20  type ApolloProvider struct {
    21  	app    core.IApp
    22  	client *apollo_sdk.ApolloClient
    23  
    24  	watchNamespaces       map[string]int          // 观察的命名空间, value为notificationID
    25  	namespaceCallbackList map[string]KeyCallbacks // 命名空间回调列表
    26  
    27  	watchCtx       context.Context
    28  	watchCtxCancel context.CancelFunc
    29  
    30  	// 用于锁 watchNamespaces, watchNamespaceList
    31  	mx sync.Mutex
    32  }
    33  
    34  // key回调列表
    35  type KeyCallbacks map[string][]core.ConfigWatchProviderCallback
    36  
    37  func (p *ApolloProvider) Inject(a ...interface{}) {}
    38  func (p *ApolloProvider) Start() error {
    39  	go p.startWatchNamespace()
    40  	return nil
    41  }
    42  func (p *ApolloProvider) Close() error {
    43  	p.watchCtxCancel()
    44  	return nil
    45  }
    46  
    47  func NewApolloProvider(app core.IApp) *ApolloProvider {
    48  	client, err := config.GetApolloClient()
    49  	if err != nil {
    50  		app.Fatal("获取客户端失败", zap.Error(err))
    51  	}
    52  	p := &ApolloProvider{
    53  		app:                   app,
    54  		client:                client,
    55  		watchNamespaces:       make(map[string]int),
    56  		namespaceCallbackList: make(map[string]KeyCallbacks),
    57  	}
    58  	p.watchCtx, p.watchCtxCancel = context.WithCancel(app.BaseContext())
    59  	return p
    60  }
    61  
    62  // 获取
    63  func (p *ApolloProvider) Get(groupName, keyName string) ([]byte, error) {
    64  	if groupName == "" {
    65  		groupName = apollo_sdk.ApplicationNamespace
    66  	}
    67  	_, data, _, err := p.client.GetNamespaceData(groupName, true)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	value, ok := data.Configurations[keyName]
    72  	if !ok {
    73  		return nil, fmt.Errorf("配置数据不存在 groupName: %s, keyName: %s", groupName, keyName)
    74  	}
    75  	return []byte(value), nil
    76  }
    77  
    78  // watch
    79  func (p *ApolloProvider) Watch(groupName, keyName string, callback core.ConfigWatchProviderCallback) error {
    80  	if groupName == "" {
    81  		groupName = apollo_sdk.ApplicationNamespace
    82  	}
    83  	_, data, _, err := p.client.GetNamespaceData(groupName, true)
    84  	if err != nil {
    85  		return err
    86  	}
    87  	_, ok := data.Configurations[keyName]
    88  	if !ok {
    89  		return fmt.Errorf("配置数据不存在 groupName: %s, keyName: %s", groupName, keyName)
    90  	}
    91  
    92  	// 添加观察命名空间
    93  	p.addWatchNamespace(groupName)
    94  	// 添加回调
    95  	p.addCallback(groupName, keyName, callback)
    96  
    97  	return nil
    98  }
    99  
   100  // 添加观察命名空间
   101  func (p *ApolloProvider) addWatchNamespace(namespace string) {
   102  	p.mx.Lock()
   103  	defer p.mx.Unlock()
   104  
   105  	_, ok := p.watchNamespaces[namespace]
   106  	if !ok {
   107  		p.watchNamespaces[namespace] = 0
   108  	}
   109  }
   110  
   111  // 添加回调
   112  func (p *ApolloProvider) addCallback(groupName, keyName string, callback core.ConfigWatchProviderCallback) {
   113  	p.mx.Lock()
   114  	defer p.mx.Unlock()
   115  
   116  	keyCallbacks, ok := p.namespaceCallbackList[groupName]
   117  	if !ok {
   118  		keyCallbacks = make(KeyCallbacks, 1)
   119  		p.namespaceCallbackList[groupName] = keyCallbacks
   120  	}
   121  	keyCallbacks[keyName] = append(keyCallbacks[keyName], callback)
   122  }
   123  
   124  // 开始观察命名空间
   125  func (p *ApolloProvider) startWatchNamespace() {
   126  	for {
   127  		select {
   128  		case <-p.watchCtx.Done():
   129  			return
   130  		default:
   131  			param := p.makeNotificationParam()
   132  			rsp, err := p.client.WaitNotification(p.watchCtx, param)
   133  			if err != nil {
   134  				logger.Log.Error("创建观察apollo通知失败", zap.Any("param", param), zap.Error(err))
   135  				time.Sleep(WatchErrWaitTime)
   136  				continue
   137  			}
   138  
   139  			// 解析通知结果
   140  			p.parseNotificationRsp(rsp)
   141  		}
   142  	}
   143  }
   144  
   145  // 构建通知param
   146  func (p *ApolloProvider) makeNotificationParam() []*apollo_sdk.NotificationParam {
   147  	p.mx.Lock()
   148  	param := make([]*apollo_sdk.NotificationParam, 0, len(p.watchNamespaces))
   149  	for k, nid := range p.watchNamespaces {
   150  		param = append(param, &apollo_sdk.NotificationParam{
   151  			NamespaceName:  k,
   152  			NotificationId: nid,
   153  		})
   154  	}
   155  	p.mx.Unlock()
   156  	return param
   157  }
   158  
   159  // 解析通知结果
   160  func (p *ApolloProvider) parseNotificationRsp(rsp []*apollo_sdk.NotificationRsp) {
   161  	if len(rsp) == 0 {
   162  		return
   163  	}
   164  
   165  	p.mx.Lock()
   166  	defer p.mx.Unlock()
   167  	for _, v := range rsp {
   168  		p.watchNamespaces[v.NamespaceName] = v.NotificationId
   169  		go p.ReReqNamespaceData(v.NamespaceName)
   170  	}
   171  }
   172  
   173  // 重新请求命名空间数据
   174  func (p *ApolloProvider) ReReqNamespaceData(namespace string) {
   175  	oldData, newData, changed, err := p.client.GetNamespaceData(namespace, true)
   176  	if err != nil {
   177  		p.app.Error("重新请求apollo命名空间数据失败", zap.String("namespace", namespace), zap.Error(err))
   178  		return
   179  	}
   180  	if !changed {
   181  		return
   182  	}
   183  
   184  	p.mx.Lock()
   185  	defer p.mx.Unlock()
   186  
   187  	// 获取回调函数列表, 遍历回调
   188  	keyCallbacks, ok := p.namespaceCallbackList[namespace]
   189  	if !ok {
   190  		return
   191  	}
   192  	for key, callbacks := range keyCallbacks {
   193  		oldVale := oldData.Configurations[key]
   194  		newValue := newData.Configurations[key]
   195  		for _, fn := range callbacks {
   196  			go fn(namespace, key, []byte(oldVale), []byte(newValue))
   197  		}
   198  	}
   199  }