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 }