github.com/wangyougui/gf/v2@v2.6.5/net/gsvc/gsvc_discovery.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/wangyougui/gf.
     6  
     7  package gsvc
     8  
     9  import (
    10  	"context"
    11  	"time"
    12  
    13  	"github.com/wangyougui/gf/v2/container/gmap"
    14  	"github.com/wangyougui/gf/v2/errors/gcode"
    15  	"github.com/wangyougui/gf/v2/errors/gerror"
    16  	"github.com/wangyougui/gf/v2/internal/intlog"
    17  	"github.com/wangyougui/gf/v2/util/gutil"
    18  )
    19  
    20  // watchedMap stores discovery object and its watched service mapping.
    21  var watchedMap = gmap.New(true)
    22  
    23  // ServiceWatch is used to watch the service status.
    24  type ServiceWatch func(service Service)
    25  
    26  // Get retrieves and returns the service by service name.
    27  func Get(ctx context.Context, name string) (service Service, err error) {
    28  	return GetAndWatchWithDiscovery(ctx, defaultRegistry, name, nil)
    29  }
    30  
    31  // GetWithDiscovery retrieves and returns the service by service name in `discovery`.
    32  func GetWithDiscovery(ctx context.Context, discovery Discovery, name string) (service Service, err error) {
    33  	return GetAndWatchWithDiscovery(ctx, discovery, name, nil)
    34  }
    35  
    36  // GetAndWatch is used to getting the service with custom watch callback function.
    37  func GetAndWatch(ctx context.Context, name string, watch ServiceWatch) (service Service, err error) {
    38  	return GetAndWatchWithDiscovery(ctx, defaultRegistry, name, watch)
    39  }
    40  
    41  // GetAndWatchWithDiscovery is used to getting the service with custom watch callback function in `discovery`.
    42  func GetAndWatchWithDiscovery(ctx context.Context, discovery Discovery, name string, watch ServiceWatch) (service Service, err error) {
    43  	if discovery == nil {
    44  		return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `discovery cannot be nil`)
    45  	}
    46  	// Retrieve service map by discovery object.
    47  	watchedServiceMap := watchedMap.GetOrSetFunc(discovery, func() interface{} {
    48  		return gmap.NewStrAnyMap(true)
    49  	}).(*gmap.StrAnyMap)
    50  	// Retrieve service by name.
    51  	storedService := watchedServiceMap.GetOrSetFuncLock(name, func() interface{} {
    52  		var (
    53  			services []Service
    54  			watcher  Watcher
    55  		)
    56  		services, err = discovery.Search(ctx, SearchInput{
    57  			Name: name,
    58  		})
    59  		if err != nil {
    60  			return nil
    61  		}
    62  		if len(services) == 0 {
    63  			err = gerror.NewCodef(gcode.CodeNotFound, `service not found with name "%s"`, name)
    64  			return nil
    65  		}
    66  
    67  		// Just pick one if multiple.
    68  		service = services[0]
    69  
    70  		// Watch the service changes in goroutine.
    71  		if watch != nil {
    72  			if watcher, err = discovery.Watch(ctx, service.GetPrefix()); err != nil {
    73  				return nil
    74  			}
    75  			go watchAndUpdateService(watchedServiceMap, watcher, service, watch)
    76  		}
    77  		return service
    78  	})
    79  	if storedService != nil {
    80  		service = storedService.(Service)
    81  	}
    82  	return
    83  }
    84  
    85  // watchAndUpdateService watches and updates the service in memory if it is changed.
    86  func watchAndUpdateService(watchedServiceMap *gmap.StrAnyMap, watcher Watcher, service Service, watchFunc ServiceWatch) {
    87  	var (
    88  		ctx      = context.Background()
    89  		err      error
    90  		services []Service
    91  	)
    92  	for {
    93  		time.Sleep(time.Second)
    94  		if services, err = watcher.Proceed(); err != nil {
    95  			intlog.Errorf(ctx, `%+v`, err)
    96  			continue
    97  		}
    98  		if len(services) > 0 {
    99  			watchedServiceMap.Set(service.GetName(), services[0])
   100  			if watchFunc != nil {
   101  				gutil.TryCatch(ctx, func(ctx context.Context) {
   102  					watchFunc(services[0])
   103  				}, func(ctx context.Context, exception error) {
   104  					intlog.Errorf(ctx, `%+v`, exception)
   105  				})
   106  			}
   107  		}
   108  	}
   109  }
   110  
   111  // Search searches and returns services with specified condition.
   112  func Search(ctx context.Context, in SearchInput) ([]Service, error) {
   113  	if defaultRegistry == nil {
   114  		return nil, gerror.NewCodef(gcode.CodeNotImplemented, `no Registry is registered`)
   115  	}
   116  	ctx, _ = context.WithTimeout(ctx, defaultTimeout)
   117  	return defaultRegistry.Search(ctx, in)
   118  }
   119  
   120  // Watch watches specified condition changes.
   121  func Watch(ctx context.Context, key string) (Watcher, error) {
   122  	if defaultRegistry == nil {
   123  		return nil, gerror.NewCodef(gcode.CodeNotImplemented, `no Registry is registered`)
   124  	}
   125  	return defaultRegistry.Watch(ctx, key)
   126  }