trpc.group/trpc-go/trpc-go@v1.0.3/config/provider.go (about)

     1  //
     2  //
     3  // Tencent is pleased to support the open source community by making tRPC available.
     4  //
     5  // Copyright (C) 2023 THL A29 Limited, a Tencent company.
     6  // All rights reserved.
     7  //
     8  // If you have downloaded a copy of the tRPC source code from Tencent,
     9  // please note that tRPC source code is licensed under the  Apache 2.0 License,
    10  // A copy of the Apache 2.0 License is included in this file.
    11  //
    12  //
    13  
    14  package config
    15  
    16  import (
    17  	"os"
    18  	"path/filepath"
    19  	"sync"
    20  
    21  	"github.com/fsnotify/fsnotify"
    22  
    23  	"trpc.group/trpc-go/trpc-go/log"
    24  )
    25  
    26  func init() {
    27  	RegisterProvider(newFileProvider())
    28  }
    29  
    30  func newFileProvider() *FileProvider {
    31  	fp := &FileProvider{
    32  		cb:              make(chan ProviderCallback),
    33  		disabledWatcher: true,
    34  		cache:           make(map[string]string),
    35  		modTime:         make(map[string]int64),
    36  	}
    37  	watcher, err := fsnotify.NewWatcher()
    38  	if err == nil {
    39  		fp.disabledWatcher = false
    40  		fp.watcher = watcher
    41  		go fp.run()
    42  		return fp
    43  	}
    44  	log.Debugf("fsnotify.NewWatcher err: %+v", err)
    45  	return fp
    46  }
    47  
    48  // FileProvider is a config provider which gets config from file system.
    49  type FileProvider struct {
    50  	disabledWatcher bool
    51  	watcher         *fsnotify.Watcher
    52  	cb              chan ProviderCallback
    53  	cache           map[string]string
    54  	modTime         map[string]int64
    55  	mu              sync.RWMutex
    56  }
    57  
    58  // Name returns file provider's name.
    59  func (*FileProvider) Name() string {
    60  	return "file"
    61  }
    62  
    63  // Read reads the specific path file, returns
    64  // it content as bytes.
    65  func (fp *FileProvider) Read(path string) ([]byte, error) {
    66  	if !fp.disabledWatcher {
    67  		if err := fp.watcher.Add(filepath.Dir(path)); err != nil {
    68  			return nil, err
    69  		}
    70  		fp.mu.Lock()
    71  		fp.cache[filepath.Clean(path)] = path
    72  		fp.mu.Unlock()
    73  	}
    74  	data, err := os.ReadFile(path)
    75  	if err != nil {
    76  		log.Tracef("Failed to read file %v", err)
    77  		return nil, err
    78  	}
    79  	return data, nil
    80  }
    81  
    82  // Watch watches config changing. The change will
    83  // be handled by callback function.
    84  func (fp *FileProvider) Watch(cb ProviderCallback) {
    85  	if !fp.disabledWatcher {
    86  		fp.cb <- cb
    87  	}
    88  }
    89  
    90  func (fp *FileProvider) run() {
    91  	fn := make([]ProviderCallback, 0)
    92  	for {
    93  		select {
    94  		case i := <-fp.cb:
    95  			fn = append(fn, i)
    96  		case e := <-fp.watcher.Events:
    97  			if t, ok := fp.isModified(e); ok {
    98  				fp.trigger(e, t, fn)
    99  			}
   100  		}
   101  	}
   102  }
   103  
   104  func (fp *FileProvider) isModified(e fsnotify.Event) (int64, bool) {
   105  	if e.Op&fsnotify.Write != fsnotify.Write {
   106  		return 0, false
   107  	}
   108  	fp.mu.RLock()
   109  	defer fp.mu.RUnlock()
   110  	if _, ok := fp.cache[filepath.Clean(e.Name)]; !ok {
   111  		return 0, false
   112  	}
   113  	fi, err := os.Stat(e.Name)
   114  	if err != nil {
   115  		return 0, false
   116  	}
   117  	if fi.ModTime().Unix() > fp.modTime[e.Name] {
   118  		return fi.ModTime().Unix(), true
   119  	}
   120  	return 0, false
   121  }
   122  
   123  func (fp *FileProvider) trigger(e fsnotify.Event, t int64, fn []ProviderCallback) {
   124  	data, err := os.ReadFile(e.Name)
   125  	if err != nil {
   126  		return
   127  	}
   128  	fp.mu.Lock()
   129  	path := fp.cache[filepath.Clean(e.Name)]
   130  	fp.modTime[e.Name] = t
   131  	fp.mu.Unlock()
   132  	for _, f := range fn {
   133  		go f(path, data)
   134  	}
   135  }