gitee.com/liuxuezhan/go-micro-v1.18.0@v1.0.0/runtime/default.go (about) 1 package runtime 2 3 import ( 4 "errors" 5 "sync" 6 "time" 7 8 "gitee.com/liuxuezhan/go-micro-v1.18.0/util/log" 9 ) 10 11 type runtime struct { 12 sync.RWMutex 13 // options configure runtime 14 options Options 15 // used to stop the runtime 16 closed chan bool 17 // used to start new services 18 start chan *service 19 // indicates if we're running 20 running bool 21 // the service map 22 // TODO: track different versions of the same service 23 services map[string]*service 24 } 25 26 // NewRuntime creates new local runtime and returns it 27 func NewRuntime(opts ...Option) Runtime { 28 // get default options 29 options := Options{} 30 31 // apply requested options 32 for _, o := range opts { 33 o(&options) 34 } 35 36 return &runtime{ 37 options: options, 38 closed: make(chan bool), 39 start: make(chan *service, 128), 40 services: make(map[string]*service), 41 } 42 } 43 44 // Init initializes runtime options 45 func (r *runtime) Init(opts ...Option) error { 46 r.Lock() 47 defer r.Unlock() 48 49 for _, o := range opts { 50 o(&r.options) 51 } 52 53 return nil 54 } 55 56 // run runs the runtime management loop 57 func (r *runtime) run(events <-chan Event) { 58 t := time.NewTicker(time.Second * 5) 59 defer t.Stop() 60 61 // process event processes an incoming event 62 processEvent := func(event Event, service *service) error { 63 // get current vals 64 r.RLock() 65 name := service.Name 66 updated := service.updated 67 r.RUnlock() 68 69 // only process if the timestamp is newer 70 if !event.Timestamp.After(updated) { 71 return nil 72 } 73 74 log.Debugf("Runtime updating service %s", name) 75 76 // this will cause a delete followed by created 77 if err := r.Update(service.Service); err != nil { 78 return err 79 } 80 81 // update the local timestamp 82 r.Lock() 83 service.updated = updated 84 r.Unlock() 85 86 return nil 87 } 88 89 for { 90 select { 91 case <-t.C: 92 // check running services 93 r.RLock() 94 for _, service := range r.services { 95 if service.Running() { 96 continue 97 } 98 99 // TODO: check service error 100 log.Debugf("Runtime starting %s", service.Name) 101 if err := service.Start(); err != nil { 102 log.Debugf("Runtime error starting %s: %v", service.Name, err) 103 } 104 } 105 r.RUnlock() 106 case service := <-r.start: 107 if service.Running() { 108 continue 109 } 110 // TODO: check service error 111 log.Debugf("Runtime starting service %s", service.Name) 112 if err := service.Start(); err != nil { 113 log.Debugf("Runtime error starting service %s: %v", service.Name, err) 114 } 115 case event := <-events: 116 log.Debugf("Runtime received notification event: %v", event) 117 // NOTE: we only handle Update events for now 118 switch event.Type { 119 case Update: 120 if len(event.Service) > 0 { 121 r.RLock() 122 service, ok := r.services[event.Service] 123 r.RUnlock() 124 if !ok { 125 log.Debugf("Runtime unknown service: %s", event.Service) 126 continue 127 } 128 if err := processEvent(event, service); err != nil { 129 log.Debugf("Runtime error updating service %s: %v", event.Service, err) 130 } 131 continue 132 } 133 134 r.RLock() 135 services := r.services 136 r.RUnlock() 137 138 // if blank service was received we update all services 139 for _, service := range services { 140 if err := processEvent(event, service); err != nil { 141 log.Debugf("Runtime error updating service %s: %v", service.Name, err) 142 } 143 } 144 } 145 case <-r.closed: 146 log.Debugf("Runtime stopped.") 147 return 148 } 149 } 150 } 151 152 // Create creates a new service which is then started by runtime 153 func (r *runtime) Create(s *Service, opts ...CreateOption) error { 154 r.Lock() 155 defer r.Unlock() 156 157 if _, ok := r.services[s.Name]; ok { 158 return errors.New("service already registered") 159 } 160 161 var options CreateOptions 162 for _, o := range opts { 163 o(&options) 164 } 165 166 if len(options.Command) == 0 { 167 return errors.New("missing exec command") 168 } 169 170 // save service 171 r.services[s.Name] = newService(s, options) 172 173 // push into start queue 174 log.Debugf("Runtime creating service %s", s.Name) 175 r.start <- r.services[s.Name] 176 177 return nil 178 } 179 180 // Read returns all instances of requested service 181 // If no service name is provided we return all the track services. 182 func (r *runtime) Read(opts ...ReadOption) ([]*Service, error) { 183 r.Lock() 184 defer r.Unlock() 185 186 gopts := ReadOptions{} 187 for _, o := range opts { 188 o(&gopts) 189 } 190 191 save := func(k, v string) bool { 192 if len(k) == 0 { 193 return true 194 } 195 return k == v 196 } 197 198 //nolint:prealloc 199 var services []*Service 200 201 for _, service := range r.services { 202 if !save(gopts.Service, service.Name) { 203 continue 204 } 205 if !save(gopts.Version, service.Version) { 206 continue 207 } 208 // TODO deal with service type 209 // no version has sbeen requested, just append the service 210 services = append(services, service.Service) 211 } 212 213 return services, nil 214 } 215 216 // Update attemps to update the service 217 func (r *runtime) Update(s *Service) error { 218 var opts []CreateOption 219 220 // check if the service already exists 221 r.RLock() 222 if service, ok := r.services[s.Name]; ok { 223 opts = append(opts, WithOutput(service.output)) 224 } 225 r.RUnlock() 226 227 // delete the service 228 if err := r.Delete(s); err != nil { 229 return err 230 } 231 232 // create new service 233 return r.Create(s, opts...) 234 } 235 236 // Delete removes the service from the runtime and stops it 237 func (r *runtime) Delete(s *Service) error { 238 r.Lock() 239 defer r.Unlock() 240 241 log.Debugf("Runtime deleting service %s", s.Name) 242 if s, ok := r.services[s.Name]; ok { 243 // check if running 244 if !s.Running() { 245 delete(r.services, s.Name) 246 return nil 247 } 248 // otherwise stop it 249 if err := s.Stop(); err != nil { 250 return err 251 } 252 // delete it 253 delete(r.services, s.Name) 254 return nil 255 } 256 257 return nil 258 } 259 260 // List returns a slice of all services tracked by the runtime 261 func (r *runtime) List() ([]*Service, error) { 262 r.RLock() 263 defer r.RUnlock() 264 265 services := make([]*Service, 0, len(r.services)) 266 267 for _, service := range r.services { 268 services = append(services, service.Service) 269 } 270 271 return services, nil 272 } 273 274 // Start starts the runtime 275 func (r *runtime) Start() error { 276 r.Lock() 277 defer r.Unlock() 278 279 // already running 280 if r.running { 281 return nil 282 } 283 284 // set running 285 r.running = true 286 r.closed = make(chan bool) 287 288 var events <-chan Event 289 if r.options.Notifier != nil { 290 var err error 291 events, err = r.options.Notifier.Notify() 292 if err != nil { 293 // TODO: should we bail here? 294 log.Debugf("Runtime failed to start update notifier") 295 } 296 } 297 298 go r.run(events) 299 300 return nil 301 } 302 303 // Stop stops the runtime 304 func (r *runtime) Stop() error { 305 r.Lock() 306 defer r.Unlock() 307 308 if !r.running { 309 return nil 310 } 311 312 select { 313 case <-r.closed: 314 return nil 315 default: 316 close(r.closed) 317 318 // set not running 319 r.running = false 320 321 // stop all the services 322 for _, service := range r.services { 323 log.Debugf("Runtime stopping %s", service.Name) 324 service.Stop() 325 } 326 // stop the notifier too 327 if r.options.Notifier != nil { 328 return r.options.Notifier.Close() 329 } 330 } 331 332 return nil 333 } 334 335 // String implements stringer interface 336 func (r *runtime) String() string { 337 return "local" 338 }