github.com/moleculer-go/moleculer@v0.3.3/broker/broker.go (about)

     1  package broker
     2  
     3  import (
     4  	"errors"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/hashicorp/go-uuid"
     9  	bus "github.com/moleculer-go/goemitter"
    10  	"github.com/moleculer-go/moleculer"
    11  	"github.com/moleculer-go/moleculer/cache"
    12  	"github.com/moleculer-go/moleculer/context"
    13  	"github.com/moleculer-go/moleculer/metrics"
    14  	"github.com/moleculer-go/moleculer/middleware"
    15  	"github.com/moleculer-go/moleculer/payload"
    16  	"github.com/moleculer-go/moleculer/registry"
    17  	"github.com/moleculer-go/moleculer/serializer"
    18  	"github.com/moleculer-go/moleculer/service"
    19  
    20  	log "github.com/sirupsen/logrus"
    21  )
    22  
    23  func mergeMaps(base, new map[string]interface{}) map[string]interface{} {
    24  	if base == nil {
    25  		base = map[string]interface{}{}
    26  	}
    27  	for key, value := range new {
    28  		base[key] = value
    29  	}
    30  	return base
    31  }
    32  
    33  func mergeConfigs(baseConfig moleculer.Config, userConfig []*moleculer.Config) moleculer.Config {
    34  	if len(userConfig) > 0 {
    35  		for _, config := range userConfig {
    36  			if config.Services != nil {
    37  				baseConfig.Services = mergeMaps(baseConfig.Services, config.Services)
    38  			}
    39  
    40  			if config.LogLevel != "" {
    41  				baseConfig.LogLevel = config.LogLevel
    42  			}
    43  			if config.LogFormat != "" {
    44  				baseConfig.LogFormat = config.LogFormat
    45  			}
    46  			if config.DiscoverNodeID != nil {
    47  				baseConfig.DiscoverNodeID = config.DiscoverNodeID
    48  			}
    49  			if config.Transporter != "" {
    50  				baseConfig.Transporter = config.Transporter
    51  			}
    52  			if config.TransporterFactory != nil {
    53  				baseConfig.TransporterFactory = config.TransporterFactory
    54  			}
    55  			if config.StrategyFactory != nil {
    56  				baseConfig.StrategyFactory = config.StrategyFactory
    57  			}
    58  			if config.DisableInternalMiddlewares {
    59  				baseConfig.DisableInternalMiddlewares = config.DisableInternalMiddlewares
    60  			}
    61  			if config.DisableInternalServices {
    62  				baseConfig.DisableInternalServices = config.DisableInternalServices
    63  			}
    64  			if config.Metrics {
    65  				baseConfig.Metrics = config.Metrics
    66  			}
    67  
    68  			if config.MetricsRate > 0 {
    69  				baseConfig.MetricsRate = config.MetricsRate
    70  			}
    71  
    72  			if config.DontWaitForNeighbours {
    73  				baseConfig.DontWaitForNeighbours = config.DontWaitForNeighbours
    74  			}
    75  
    76  			if config.Middlewares != nil {
    77  				baseConfig.Middlewares = config.Middlewares
    78  			}
    79  			if config.RequestTimeout != 0 {
    80  				baseConfig.RequestTimeout = config.RequestTimeout
    81  			}
    82  
    83  			if config.Namespace != "" {
    84  				baseConfig.Namespace = config.Namespace
    85  			}
    86  		}
    87  	}
    88  	return baseConfig
    89  }
    90  
    91  type ServiceBroker struct {
    92  	namespace string
    93  
    94  	logger *log.Entry
    95  
    96  	localBus *bus.Emitter
    97  
    98  	registry *registry.ServiceRegistry
    99  
   100  	middlewares *middleware.Dispatch
   101  
   102  	cache cache.Cache
   103  
   104  	serializer *serializer.Serializer
   105  
   106  	services []*service.Service
   107  
   108  	started  bool
   109  	starting bool
   110  
   111  	rootContext moleculer.BrokerContext
   112  
   113  	config moleculer.Config
   114  
   115  	delegates *moleculer.BrokerDelegates
   116  
   117  	id string
   118  
   119  	instanceID string
   120  
   121  	localNode moleculer.Node
   122  }
   123  
   124  // GetLocalBus : return the service broker local bus (Event Emitter)
   125  func (broker *ServiceBroker) LocalBus() *bus.Emitter {
   126  	return broker.localBus
   127  }
   128  
   129  // stopService stop the service.
   130  func (broker *ServiceBroker) stopService(svc *service.Service) {
   131  	broker.middlewares.CallHandlers("serviceStopping", svc)
   132  	svc.Stop(broker.rootContext.ChildActionContext("service.stop", payload.Empty()))
   133  	broker.middlewares.CallHandlers("serviceStopped", svc)
   134  }
   135  
   136  // applyServiceConfig apply broker config to the service configuration
   137  // settings is an import config copy from broker to the service.
   138  func (broker *ServiceBroker) applyServiceConfig(svc *service.Service) {
   139  	if bkrConfig, exists := broker.config.Services[svc.Name()]; exists {
   140  		svcConfig, ok := bkrConfig.(map[string]interface{})
   141  		if ok {
   142  			_, ok := svcConfig["settings"]
   143  			if ok {
   144  				settings, ok := svcConfig["settings"].(map[string]interface{})
   145  				if ok {
   146  					svc.AddSettings(settings)
   147  				} else {
   148  					broker.logger.Error("Could not add service settings - Error converting the input settings to map[string]interface{} - Invalid format! Service Config : ", svcConfig)
   149  				}
   150  			}
   151  
   152  			_, ok = svcConfig["metadata"]
   153  			if ok {
   154  				metadata, ok := svcConfig["metadata"].(map[string]interface{})
   155  				if ok {
   156  					svc.AddSettings(metadata)
   157  				} else {
   158  					broker.logger.Error("Could not add service metadata - Error converting the input metadata to map[string]interface{} - Invalid format! Service Config : ", svcConfig)
   159  				}
   160  			}
   161  		} else {
   162  			broker.logger.Error("Could not apply service configuration - Error converting the service config to map[string]interface{} - Invalid format! Broker Config : ", bkrConfig)
   163  		}
   164  
   165  	}
   166  }
   167  
   168  // startService start a service.
   169  func (broker *ServiceBroker) startService(svc *service.Service) {
   170  
   171  	broker.logger.Debug("Broker start service: ", svc.FullName())
   172  
   173  	broker.applyServiceConfig(svc)
   174  
   175  	broker.middlewares.CallHandlers("serviceStarting", svc)
   176  
   177  	broker.waitForDependencies(svc)
   178  
   179  	broker.registry.AddLocalService(svc)
   180  
   181  	broker.middlewares.CallHandlers("serviceStarted", svc)
   182  
   183  	svc.Start(broker.rootContext.ChildActionContext("service.start", payload.Empty()))
   184  }
   185  
   186  // waitForDependencies wait for all services listed in the service dependencies to be discovered.
   187  func (broker *ServiceBroker) waitForDependencies(service *service.Service) {
   188  	if len(service.Dependencies()) == 0 {
   189  		return
   190  	}
   191  	start := time.Now()
   192  	for {
   193  		if !broker.started {
   194  			break
   195  		}
   196  		found := true
   197  		for _, dependency := range service.Dependencies() {
   198  			known := broker.registry.KnowService(dependency)
   199  			if !known {
   200  				found = false
   201  				break
   202  			}
   203  		}
   204  		if found {
   205  			broker.logger.Debug("waitForDependencies() - All dependencies were found :) -> service: ", service.Name(), " wait For Dependencies: ", service.Dependencies())
   206  			break
   207  		}
   208  		if time.Since(start) > broker.config.WaitForDependenciesTimeout {
   209  			broker.logger.Warn("waitForDependencies() - Time out ! service: ", service.Name(), " wait For Dependencies: ", service.Dependencies())
   210  			break
   211  		}
   212  		time.Sleep(time.Microsecond)
   213  	}
   214  }
   215  
   216  func (broker *ServiceBroker) broadcastLocal(eventName string, params ...interface{}) {
   217  	broker.LocalBus().EmitAsync(eventName, params)
   218  }
   219  
   220  func (broker *ServiceBroker) createBrokerLogger() *log.Entry {
   221  	if strings.ToUpper(broker.config.LogFormat) == "JSON" {
   222  		log.SetFormatter(&log.JSONFormatter{})
   223  	} else {
   224  		log.SetFormatter(&log.TextFormatter{
   225  			DisableColors:             false,
   226  			ForceColors:               true,
   227  			EnvironmentOverrideColors: true,
   228  		})
   229  	}
   230  
   231  	if strings.ToUpper(broker.config.LogLevel) == "WARN" {
   232  		log.SetLevel(log.WarnLevel)
   233  	} else if strings.ToUpper(broker.config.LogLevel) == "DEBUG" {
   234  		log.SetLevel(log.DebugLevel)
   235  	} else if strings.ToUpper(broker.config.LogLevel) == "TRACE" {
   236  		log.SetLevel(log.TraceLevel)
   237  	} else if strings.ToUpper(broker.config.LogLevel) == "ERROR" {
   238  		log.SetLevel(log.ErrorLevel)
   239  	} else if strings.ToUpper(broker.config.LogLevel) == "FATAL" {
   240  		log.SetLevel(log.FatalLevel)
   241  	} else {
   242  		log.SetLevel(log.InfoLevel)
   243  	}
   244  
   245  	brokerLogger := log.WithFields(log.Fields{
   246  		"broker": broker.id,
   247  	})
   248  	//broker.logger.Debug("Broker Log Setup -> Level", log.GetLevel(), " nodeID: ", nodeID)
   249  	return brokerLogger
   250  }
   251  
   252  // addService internal addService .. adds one service.Service instance to broker.services list.
   253  func (broker *ServiceBroker) addService(svc *service.Service) {
   254  	svc.SetNodeID(broker.localNode.GetID())
   255  	broker.services = append(broker.services, svc)
   256  	if broker.started || broker.starting {
   257  		broker.startService(svc)
   258  	}
   259  	broker.logger.Debug("Broker - addService() - fullname: ", svc.FullName(), " # actions: ", len(svc.Actions()), " # events: ", len(svc.Events()))
   260  }
   261  
   262  // resolveSchema getting schema from interface
   263  func (broker *ServiceBroker) resolveSchema(svc interface{}) (moleculer.ServiceSchema, bool) {
   264  	schema, isSchema := svc.(moleculer.ServiceSchema)
   265  	if !isSchema {
   266  		s, ok := svc.(*moleculer.ServiceSchema)
   267  		if ok {
   268  			schema = *s
   269  			isSchema = ok
   270  		}
   271  	}
   272  
   273  	return schema, isSchema
   274  }
   275  
   276  // createService create a new service instance, from a struct or a schema :)
   277  func (broker *ServiceBroker) createService(svc interface{}) (*service.Service, error) {
   278  
   279  	schema, isSchema := broker.resolveSchema(svc)
   280  
   281  	if !isSchema {
   282  		svc, err := service.FromObject(svc, broker.delegates)
   283  		if err != nil {
   284  			return nil, err
   285  		}
   286  		return svc, nil
   287  	}
   288  	return service.FromSchema(schema, broker.delegates), nil
   289  }
   290  
   291  // WaitFor : wait for all services to be available
   292  func (broker *ServiceBroker) WaitFor(services ...string) error {
   293  	for _, svc := range services {
   294  		if err := broker.waitForService(svc); err != nil {
   295  			return err
   296  		}
   297  	}
   298  	return nil
   299  }
   300  
   301  // WaitForNodes : wait for all nodes to be available
   302  func (broker *ServiceBroker) WaitForNodes(nodes ...string) error {
   303  	for _, nodeID := range nodes {
   304  		if err := broker.waitForNode(nodeID); err != nil {
   305  			return err
   306  		}
   307  	}
   308  	return nil
   309  }
   310  
   311  func (broker *ServiceBroker) KnowAction(action string) bool {
   312  	return broker.registry.KnowAction(action)
   313  }
   314  
   315  // WaitForActions : wait for all actions to be available
   316  func (broker *ServiceBroker) WaitForActions(actions ...string) error {
   317  	for _, action := range actions {
   318  		if err := broker.waitAction(action); err != nil {
   319  			return err
   320  		}
   321  	}
   322  	return nil
   323  }
   324  
   325  // waitForService wait for a service to be available
   326  func (broker *ServiceBroker) waitForService(service string) error {
   327  	start := time.Now()
   328  	for {
   329  		if broker.registry.KnowService(service) {
   330  			break
   331  		}
   332  		if time.Since(start) > broker.config.WaitForDependenciesTimeout {
   333  			err := errors.New("waitForService() - Timeout ! service: " + service)
   334  			broker.logger.Error(err)
   335  			return err
   336  		}
   337  		time.Sleep(time.Microsecond)
   338  	}
   339  	return nil
   340  }
   341  
   342  // waitAction wait for an action to be available
   343  func (broker *ServiceBroker) waitAction(action string) error {
   344  	start := time.Now()
   345  	for {
   346  		if broker.registry.KnowAction(action) {
   347  			break
   348  		}
   349  		if time.Since(start) > broker.config.WaitForDependenciesTimeout {
   350  			err := errors.New("waitAction() - Timeout ! action: " + action)
   351  			broker.logger.Error(err)
   352  			return err
   353  		}
   354  		time.Sleep(time.Microsecond)
   355  	}
   356  	return nil
   357  }
   358  
   359  // waitForNode wait for a node to be available
   360  func (broker *ServiceBroker) waitForNode(nodeID string) error {
   361  	start := time.Now()
   362  	for {
   363  		if broker.registry.KnowNode(nodeID) {
   364  			break
   365  		}
   366  		if time.Since(start) > broker.config.WaitForDependenciesTimeout {
   367  			err := errors.New("waitForNode() - Timeout ! nodeID: " + nodeID)
   368  			broker.logger.Error(err)
   369  			return err
   370  		}
   371  		time.Sleep(time.Microsecond)
   372  	}
   373  	return nil
   374  }
   375  
   376  // Publish : for each service schema it will validate and create
   377  // a service instance in the broker.
   378  func (broker *ServiceBroker) Publish(services ...interface{}) {
   379  	for _, item := range services {
   380  		svc, err := broker.createService(item)
   381  		if err != nil {
   382  			panic(errors.New("Could not publish service - error: " + err.Error()))
   383  		}
   384  		broker.addService(svc)
   385  	}
   386  }
   387  
   388  func (broker *ServiceBroker) Start() {
   389  	if broker.IsStarted() {
   390  		broker.logger.Warn("broker.Start() called on a broker that already started!")
   391  		return
   392  	}
   393  	broker.starting = true
   394  	broker.logger.Info("Moleculer is starting...")
   395  	broker.logger.Info("Node ID: ", broker.localNode.GetID())
   396  
   397  	broker.middlewares.CallHandlers("brokerStarting", broker.delegates)
   398  
   399  	broker.registry.Start()
   400  
   401  	internalServices := broker.registry.LocalServices()
   402  	for _, service := range internalServices {
   403  		service.SetNodeID(broker.localNode.GetID())
   404  		broker.startService(service)
   405  	}
   406  
   407  	for _, service := range broker.services {
   408  		broker.startService(service)
   409  	}
   410  
   411  	for _, service := range internalServices {
   412  		broker.addService(service)
   413  	}
   414  
   415  	broker.logger.Debug("Broker -> registry started!")
   416  
   417  	defer broker.broadcastLocal("$broker.started")
   418  	defer broker.middlewares.CallHandlers("brokerStarted", broker.delegates)
   419  
   420  	broker.started = true
   421  	broker.starting = false
   422  	broker.logger.Info("Service Broker with ", len(broker.services), " service(s) started successfully.")
   423  }
   424  
   425  func (broker *ServiceBroker) Stop() {
   426  	if !broker.started {
   427  		broker.logger.Info("Broker is not started!")
   428  		return
   429  	}
   430  	broker.logger.Info("Service Broker is stopping...")
   431  
   432  	broker.middlewares.CallHandlers("brokerStopping", broker.delegates)
   433  
   434  	for _, service := range broker.services {
   435  		broker.stopService(service)
   436  	}
   437  
   438  	broker.registry.Stop()
   439  
   440  	broker.started = false
   441  	broker.broadcastLocal("$broker.stopped")
   442  
   443  	broker.middlewares.CallHandlers("brokerStopped", broker.delegates)
   444  }
   445  
   446  type callPair struct {
   447  	label  string
   448  	result moleculer.Payload
   449  }
   450  
   451  func (broker *ServiceBroker) invokeMCalls(callMaps map[string]map[string]interface{}, result chan map[string]moleculer.Payload) {
   452  	if len(callMaps) == 0 {
   453  		result <- make(map[string]moleculer.Payload)
   454  		return
   455  	}
   456  
   457  	resultChan := make(chan callPair)
   458  	for label, content := range callMaps {
   459  		go func(label, actionName string, params interface{}, results chan callPair) {
   460  			result := <-broker.Call(actionName, params)
   461  			results <- callPair{label, result}
   462  		}(label, content["action"].(string), content["params"], resultChan)
   463  	}
   464  
   465  	timeoutChan := make(chan bool, 1)
   466  	go func(timeout time.Duration) {
   467  		time.Sleep(timeout)
   468  		timeoutChan <- true
   469  	}(broker.config.MCallTimeout)
   470  
   471  	results := make(map[string]moleculer.Payload)
   472  	for {
   473  		select {
   474  		case pair := <-resultChan:
   475  			results[pair.label] = pair.result
   476  			if len(results) == len(callMaps) {
   477  				result <- results
   478  				return
   479  			}
   480  		case <-timeoutChan:
   481  			timeoutError := errors.New("MCall timeout error.")
   482  			broker.logger.Error(timeoutError)
   483  			for label, _ := range callMaps {
   484  				if _, exists := results[label]; !exists {
   485  					results[label] = payload.New(timeoutError)
   486  				}
   487  			}
   488  			result <- results
   489  			return
   490  		}
   491  	}
   492  }
   493  
   494  // MCall perform multiple calls and return all results together in a nice map indexed by name.
   495  func (broker *ServiceBroker) MCall(callMaps map[string]map[string]interface{}) chan map[string]moleculer.Payload {
   496  	result := make(chan map[string]moleculer.Payload, 1)
   497  	go broker.invokeMCalls(callMaps, result)
   498  	return result
   499  }
   500  
   501  // Call :  invoke a service action and return a channel which will eventualy deliver the results ;)
   502  func (broker *ServiceBroker) Call(actionName string, params interface{}, opts ...moleculer.Options) chan moleculer.Payload {
   503  	broker.logger.Trace("Broker - Call() actionName: ", actionName, " params: ", params, " opts: ", opts)
   504  	if !broker.IsStarted() {
   505  		panic(errors.New("Broker must be started before making calls :("))
   506  	}
   507  	actionContext := broker.rootContext.ChildActionContext(actionName, payload.New(params), opts...)
   508  	return broker.registry.LoadBalanceCall(actionContext, opts...)
   509  }
   510  
   511  func (broker *ServiceBroker) Emit(event string, params interface{}, groups ...string) {
   512  	broker.logger.Trace("Broker - Emit() event: ", event, " params: ", params, " groups: ", groups)
   513  	if !broker.IsStarted() {
   514  		panic(errors.New("Broker must be started before emiting events :("))
   515  	}
   516  	newContext := broker.rootContext.ChildEventContext(event, payload.New(params), groups, false)
   517  	broker.registry.LoadBalanceEvent(newContext)
   518  }
   519  
   520  func (broker *ServiceBroker) Broadcast(event string, params interface{}, groups ...string) {
   521  	broker.logger.Trace("Broker - Broadcast() event: ", event, " params: ", params, " groups: ", groups)
   522  	if !broker.IsStarted() {
   523  		panic(errors.New("Broker must be started before broadcasting events :("))
   524  	}
   525  	newContext := broker.rootContext.ChildEventContext(event, payload.New(params), groups, true)
   526  	broker.registry.BroadcastEvent(newContext)
   527  }
   528  
   529  func (broker *ServiceBroker) IsStarted() bool {
   530  	return broker.started
   531  }
   532  
   533  func (broker *ServiceBroker) GetLogger(name string, value string) *log.Entry {
   534  	return broker.logger.WithField(name, value)
   535  }
   536  
   537  func (broker *ServiceBroker) LocalNode() moleculer.Node {
   538  	return broker.localNode
   539  }
   540  
   541  func (broker *ServiceBroker) newLogger(name string, value string) *log.Entry {
   542  	return broker.logger.WithField(name, value)
   543  }
   544  
   545  func (broker *ServiceBroker) setupLocalBus() {
   546  	broker.localBus = bus.Construct()
   547  
   548  	broker.localBus.On("$registry.service.added", func(args ...interface{}) {
   549  		//TODO check code from -> this.broker.servicesChanged(true)
   550  	})
   551  }
   552  
   553  func (broker *ServiceBroker) registerMiddlewares() {
   554  	broker.middlewares = middleware.Dispatcher(broker.logger.WithField("middleware", "dispatcher"))
   555  	for _, mware := range broker.config.Middlewares {
   556  		broker.middlewares.Add(mware)
   557  	}
   558  	if !broker.config.DisableInternalMiddlewares {
   559  		broker.registerInternalMiddlewares()
   560  	}
   561  }
   562  
   563  func (broker *ServiceBroker) registerInternalMiddlewares() {
   564  	broker.middlewares.Add(metrics.Middlewares())
   565  }
   566  
   567  func (broker *ServiceBroker) init() {
   568  	broker.id = broker.config.DiscoverNodeID()
   569  	broker.logger = broker.createBrokerLogger()
   570  	broker.setupLocalBus()
   571  
   572  	broker.registerMiddlewares()
   573  
   574  	broker.config = broker.middlewares.CallHandlers("Config", broker.config).(moleculer.Config)
   575  
   576  	instanceID, err := uuid.GenerateUUID()
   577  	if err != nil {
   578  		broker.logger.Error("Could not create an instance id -  error ", err)
   579  		instanceID = "error creating instance id"
   580  	}
   581  	broker.instanceID = instanceID
   582  
   583  	broker.delegates = broker.createDelegates()
   584  	broker.registry = registry.CreateRegistry(broker.id, broker.delegates)
   585  	broker.localNode = broker.registry.LocalNode()
   586  	broker.rootContext = context.BrokerContext(broker.delegates)
   587  
   588  }
   589  
   590  func (broker *ServiceBroker) createDelegates() *moleculer.BrokerDelegates {
   591  	return &moleculer.BrokerDelegates{
   592  		LocalNode: broker.LocalNode,
   593  		Logger:    broker.newLogger,
   594  		Bus:       broker.LocalBus,
   595  		IsStarted: broker.IsStarted,
   596  		Config:    broker.config,
   597  		InstanceID: func() string {
   598  			return broker.instanceID
   599  		},
   600  		ActionDelegate: func(context moleculer.BrokerContext, opts ...moleculer.Options) chan moleculer.Payload {
   601  			return broker.registry.LoadBalanceCall(context, opts...)
   602  		},
   603  		EmitEvent: func(context moleculer.BrokerContext) {
   604  			broker.registry.LoadBalanceEvent(context)
   605  		},
   606  		BroadcastEvent: func(context moleculer.BrokerContext) {
   607  			broker.registry.BroadcastEvent(context)
   608  		},
   609  		HandleRemoteEvent: func(context moleculer.BrokerContext) {
   610  			broker.registry.HandleRemoteEvent(context)
   611  		},
   612  		ServiceForAction: func(name string) []*moleculer.ServiceSchema {
   613  			svcs := broker.registry.ServiceForAction(name)
   614  			if svcs != nil {
   615  				result := make([]*moleculer.ServiceSchema, len(svcs))
   616  				for i, svc := range svcs {
   617  					result[i] = svc.Schema()
   618  				}
   619  				return result
   620  			}
   621  			return nil
   622  		},
   623  		MultActionDelegate: func(callMaps map[string]map[string]interface{}) chan map[string]moleculer.Payload {
   624  			return broker.MCall(callMaps)
   625  		},
   626  		BrokerContext: func() moleculer.BrokerContext {
   627  			return broker.rootContext
   628  		},
   629  		MiddlewareHandler: broker.middlewares.CallHandlers,
   630  		Publish:           broker.Publish,
   631  		WaitFor:           broker.WaitFor,
   632  	}
   633  }
   634  
   635  // New : returns a valid broker based on environment configuration
   636  // this is usually called when creating a broker to starting the service(s)
   637  func New(userConfig ...*moleculer.Config) *ServiceBroker {
   638  	config := mergeConfigs(moleculer.DefaultConfig, userConfig)
   639  	broker := ServiceBroker{config: config}
   640  	broker.init()
   641  	return &broker
   642  }