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 }