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  }