github.com/moleculer-go/moleculer@v0.3.3/registry/registry.go (about) 1 package registry 2 3 import ( 4 "errors" 5 "fmt" 6 "sort" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/moleculer-go/moleculer/middleware" 12 13 "github.com/moleculer-go/moleculer/payload" 14 15 "github.com/moleculer-go/moleculer" 16 "github.com/moleculer-go/moleculer/service" 17 "github.com/moleculer-go/moleculer/strategy" 18 19 "github.com/moleculer-go/moleculer/transit" 20 "github.com/moleculer-go/moleculer/transit/pubsub" 21 log "github.com/sirupsen/logrus" 22 ) 23 24 type messageHandlerFunc func(message moleculer.Payload) 25 26 type ServiceRegistry struct { 27 logger *log.Entry 28 transit transit.Transit 29 localNode moleculer.Node 30 nodes *NodeCatalog 31 services *ServiceCatalog 32 actions *ActionCatalog 33 events *EventCatalog 34 broker *moleculer.BrokerDelegates 35 strategy strategy.Strategy 36 stopping bool 37 heartbeatFrequency time.Duration 38 heartbeatTimeout time.Duration 39 offlineCheckFrequency time.Duration 40 offlineTimeout time.Duration 41 nodeReceivedMutex *sync.Mutex 42 namespace string 43 } 44 45 // createTransit create a transit instance based on the config. 46 func createTransit(broker *moleculer.BrokerDelegates) transit.Transit { 47 transit := pubsub.Create(broker) 48 return transit 49 } 50 51 // createStrategy create a strategy instance based on the config. 52 func createStrategy(broker *moleculer.BrokerDelegates) strategy.Strategy { 53 //TODO: when new strategies are addes.. adde config check here to load the right one. 54 if broker.Config.StrategyFactory != nil { 55 return broker.Config.StrategyFactory().(strategy.Strategy) 56 } 57 58 return strategy.RandomStrategy{} 59 } 60 61 func CreateRegistry(nodeID string, broker *moleculer.BrokerDelegates) *ServiceRegistry { 62 config := broker.Config 63 transit := createTransit(broker) 64 strategy := createStrategy(broker) 65 logger := broker.Logger("registry", nodeID) 66 localNode := CreateNode(nodeID, true, logger.WithField("Node", nodeID)) 67 localNode.Unavailable() 68 registry := &ServiceRegistry{ 69 broker: broker, 70 transit: transit, 71 strategy: strategy, 72 logger: logger, 73 localNode: localNode, 74 actions: CreateActionCatalog(logger.WithField("catalog", "Actions")), 75 events: CreateEventCatalog(logger.WithField("catalog", "Events")), 76 services: CreateServiceCatalog(logger.WithField("catalog", "Services")), 77 nodes: CreateNodesCatalog(logger.WithField("catalog", "Nodes")), 78 heartbeatFrequency: config.HeartbeatFrequency, 79 heartbeatTimeout: config.HeartbeatTimeout, 80 offlineCheckFrequency: config.OfflineCheckFrequency, 81 offlineTimeout: config.OfflineTimeout, 82 stopping: false, 83 nodeReceivedMutex: &sync.Mutex{}, 84 namespace: config.Namespace, 85 } 86 87 registry.logger.Debug("Service Registry created for broker: ", nodeID) 88 89 broker.Bus().On("$broker.started", func(args ...interface{}) { 90 registry.logger.Debug("Registry -> $broker.started event") 91 registry.localNode.Available() 92 }) 93 94 registry.setupMessageHandlers() 95 96 return registry 97 } 98 99 func (registry *ServiceRegistry) KnowService(name string) bool { 100 return registry.services.FindByName(name) 101 } 102 103 func (registry *ServiceRegistry) KnowAction(name string) bool { 104 return registry.actions.Find(name) != nil 105 } 106 107 func (registry *ServiceRegistry) KnowNode(nodeID string) bool { 108 _, found := registry.nodes.findNode(nodeID) 109 return found 110 } 111 112 func (registry *ServiceRegistry) LocalNode() moleculer.Node { 113 return registry.localNode 114 } 115 116 func (registry *ServiceRegistry) setupMessageHandlers() { 117 messageHandler := map[string]messageHandlerFunc{ 118 "HEARTBEAT": registry.filterMessages(registry.heartbeatMessageReceived), 119 "DISCONNECT": registry.filterMessages(registry.disconnectMessageReceived), 120 "INFO": registry.filterMessages(registry.remoteNodeInfoReceived), 121 } 122 registry.broker.Bus().On("$registry.transit.message", func(args ...interface{}) { 123 registry.logger.Trace("Registry -> $registry.transit.message event - args: ", args) 124 command := args[0].(string) 125 message := args[1].(moleculer.Payload) 126 handler := messageHandler[command] 127 if handler == nil { 128 panic(errors.New(fmt.Sprint("Registry - $registry.transit.message event - invalid command:", command))) 129 } 130 handler(message) 131 }) 132 } 133 134 func (registry *ServiceRegistry) Stop() { 135 registry.logger.Debug("Registry Stopping...") 136 registry.stopping = true 137 err := <-registry.transit.Disconnect() 138 registry.localNode.Unavailable() 139 if err != nil { 140 registry.logger.Debug("Error trying to disconnect transit - error: ", err) 141 return 142 } 143 registry.logger.Debug("Transit Disconnected -> Registry Full Stop!") 144 } 145 146 func (registry *ServiceRegistry) LocalServices() []*service.Service { 147 return []*service.Service{createNodeService(registry)} 148 } 149 150 // Start : start the registry background processes. 151 func (registry *ServiceRegistry) Start() { 152 registry.logger.Debug("Registry Start() ") 153 registry.stopping = false 154 err := <-registry.transit.Connect() 155 if err != nil { 156 panic(errors.New(fmt.Sprint("Could not connect to the transit. err: ", err))) 157 } 158 <-registry.transit.DiscoverNodes() 159 160 registry.nodes.Add(registry.localNode) 161 162 if registry.heartbeatFrequency > 0 { 163 go registry.loopWhileAlive(registry.heartbeatFrequency, registry.transit.SendHeartbeat) 164 } 165 if registry.heartbeatTimeout > 0 { 166 go registry.loopWhileAlive(registry.heartbeatTimeout, registry.checkExpiredRemoteNodes) 167 } 168 if registry.offlineCheckFrequency > 0 { 169 go registry.loopWhileAlive(registry.offlineCheckFrequency, registry.checkOfflineNodes) 170 } 171 } 172 173 func (registry *ServiceRegistry) ServiceForAction(name string) []*service.Service { 174 actions := registry.actions.Find(name) 175 if actions != nil { 176 result := make([]*service.Service, len(actions)) 177 for i, action := range actions { 178 result[i] = action.Service() 179 } 180 return result 181 } 182 return nil 183 } 184 185 // HandleRemoteEvent handle when a remote event is delivered and call all the local handlers. 186 func (registry *ServiceRegistry) HandleRemoteEvent(context moleculer.BrokerContext) { 187 name := context.EventName() 188 groups := context.Groups() 189 if registry.stopping { 190 registry.logger.Error("HandleRemoteEvent() - registry is stopping. Discarding event -> name: ", name, " groups: ", groups) 191 return 192 } 193 broadcast := context.IsBroadcast() 194 registry.logger.Debug("HandleRemoteEvent() - name: ", name, " groups: ", groups) 195 196 var stg strategy.Strategy 197 if !broadcast { 198 stg = registry.strategy 199 } 200 entries := registry.events.Find(name, groups, true, true, stg) 201 for _, localEvent := range entries { 202 go localEvent.emitLocalEvent(context) 203 } 204 } 205 206 // LoadBalanceEvent load balance an event based on the known targetNodes. 207 func (registry *ServiceRegistry) LoadBalanceEvent(context moleculer.BrokerContext) []*EventEntry { 208 name := context.EventName() 209 params := context.Payload() 210 groups := context.Groups() 211 eventSig := fmt.Sprint("name: ", name, " groups: ", groups) 212 registry.logger.Trace("LoadBalanceEvent() - ", eventSig, " params: ", params) 213 214 entries := registry.events.Find(name, groups, true, false, registry.strategy) 215 if entries == nil { 216 msg := fmt.Sprint("Broker - no endpoints found for event: ", name, " it was discarded!") 217 registry.logger.Warn(msg) 218 return nil 219 } 220 221 for _, eventEntry := range entries { 222 if eventEntry.isLocal { 223 eventEntry.emitLocalEvent(context) 224 } else { 225 registry.emitRemoteEvent(context, eventEntry) 226 } 227 } 228 registry.logger.Trace("LoadBalanceEvent() - ", eventSig, " End.") 229 return entries 230 } 231 232 func (registry *ServiceRegistry) BroadcastEvent(context moleculer.BrokerContext) []*EventEntry { 233 name := context.EventName() 234 groups := context.Groups() 235 eventSig := fmt.Sprint("name: ", name, " groups: ", groups) 236 registry.logger.Trace("BroadcastEvent() - ", eventSig, " payload: ", context.Payload()) 237 238 entries := registry.events.Find(name, groups, false, false, nil) 239 if entries == nil { 240 msg := fmt.Sprint("Broker - no endpoints found for event: ", name, " it was discarded!") 241 registry.logger.Warn(msg) 242 return nil 243 } 244 245 for _, eventEntry := range entries { 246 if eventEntry.isLocal { 247 eventEntry.emitLocalEvent(context) 248 } else { 249 registry.emitRemoteEvent(context, eventEntry) 250 } 251 } 252 registry.logger.Trace("BroadcastEvent() - ", eventSig, " End.") 253 return entries 254 } 255 256 // DelegateCall : invoke a service action and return a channel which will eventualy deliver the results ;). 257 // This call might be local or remote. 258 func (registry *ServiceRegistry) LoadBalanceCall(context moleculer.BrokerContext, opts ...moleculer.Options) chan moleculer.Payload { 259 actionName := context.ActionName() 260 params := context.Payload() 261 262 registry.logger.Trace("LoadBalanceCall() - actionName: ", actionName, " params: ", params, " namespace: ", registry.namespace, " opts: ", opts) 263 264 actionEntry := registry.nextAction(actionName, registry.strategy, opts...) 265 if actionEntry == nil { 266 msg := "Registry - endpoint not found for actionName: " + actionName 267 if registry.namespace != "" { 268 msg = msg + " namespace: " + registry.namespace 269 } 270 registry.logger.Error(msg) 271 resultChan := make(chan moleculer.Payload, 1) 272 resultChan <- payload.Error(msg) 273 return resultChan 274 } 275 registry.logger.Debug("LoadBalanceCall() - actionName: ", actionName, " target nodeID: ", actionEntry.TargetNodeID()) 276 277 if actionEntry.isLocal { 278 registry.broker.MiddlewareHandler("beforeLocalAction", context) 279 result := <-actionEntry.invokeLocalAction(context) 280 tempParams := registry.broker.MiddlewareHandler("afterLocalAction", middleware.AfterActionParams{context, result}) 281 actionParams := tempParams.(middleware.AfterActionParams) 282 283 resultChan := make(chan moleculer.Payload, 1) 284 resultChan <- actionParams.Result 285 return resultChan 286 } 287 288 registry.broker.MiddlewareHandler("beforeRemoteAction", context) 289 result := <-registry.invokeRemoteAction(context, actionEntry) 290 tempParams := registry.broker.MiddlewareHandler("afterRemoteAction", middleware.AfterActionParams{context, result}) 291 actionParams := tempParams.(middleware.AfterActionParams) 292 293 resultChan := make(chan moleculer.Payload, 1) 294 resultChan <- actionParams.Result 295 return resultChan 296 297 } 298 299 func (registry *ServiceRegistry) emitRemoteEvent(context moleculer.BrokerContext, eventEntry *EventEntry) { 300 context.SetTargetNodeID(eventEntry.TargetNodeID()) 301 registry.logger.Trace("Before invoking remote event: ", context.EventName(), " context.TargetNodeID: ", context.TargetNodeID(), " context.Payload(): ", context.Payload()) 302 registry.transit.Emit(context) 303 } 304 305 func (registry *ServiceRegistry) invokeRemoteAction(context moleculer.BrokerContext, actionEntry *ActionEntry) chan moleculer.Payload { 306 result := make(chan moleculer.Payload, 1) 307 context.SetTargetNodeID(actionEntry.TargetNodeID()) 308 registry.logger.Trace("Before invoking remote action: ", context.ActionName(), " context.TargetNodeID: ", context.TargetNodeID(), " context.Payload(): ", context.Payload()) 309 310 go func() { 311 actionResult := <-registry.transit.Request(context) 312 registry.logger.Trace("remote request done! action: ", context.ActionName(), " results: ", actionResult) 313 if registry.stopping { 314 registry.logger.Error("invokeRemoteAction() - registry is stopping. Discarding action result -> name: ", context.ActionName()) 315 result <- payload.New(errors.New("can't complete request! registry stopping...")) 316 } else { 317 result <- actionResult 318 } 319 }() 320 return result 321 } 322 323 // removeServicesByNodeID 324 func (registry *ServiceRegistry) removeServicesByNodeID(nodeID string) { 325 svcs := registry.services.RemoveByNode(nodeID) 326 if len(svcs) > 0 { 327 for _, svc := range svcs { 328 registry.broker.Bus().EmitAsync( 329 "$registry.service.removed", 330 []interface{}{svc.Summary()}) 331 } 332 } 333 registry.actions.RemoveByNode(nodeID) 334 registry.events.RemoveByNode(nodeID) 335 } 336 337 // disconnectNode remove node info (actions, events) from local registry. 338 func (registry *ServiceRegistry) disconnectNode(nodeID string) { 339 node, exists := registry.nodes.findNode(nodeID) 340 if !exists { 341 return 342 } 343 registry.removeServicesByNodeID(nodeID) 344 node.Unavailable() 345 registry.broker.Bus().EmitAsync("$node.disconnected", []interface{}{nodeID}) 346 registry.logger.Warnf("Node %s disconnected ", nodeID) 347 } 348 349 func (registry *ServiceRegistry) checkExpiredRemoteNodes() { 350 expiredNodes := registry.nodes.expiredNodes(registry.heartbeatTimeout) 351 for _, node := range expiredNodes { 352 registry.disconnectNode(node.GetID()) 353 } 354 } 355 356 func (registry *ServiceRegistry) checkOfflineNodes() { 357 expiredNodes := registry.nodes.expiredNodes(registry.offlineTimeout) 358 for _, node := range expiredNodes { 359 nodeID := node.GetID() 360 registry.nodes.removeNode(nodeID) 361 registry.logger.Warnf("Removed offline Node: %s from the registry because it hasn't submitted heartbeat in %d seconds.", nodeID, registry.offlineTimeout) 362 } 363 } 364 365 // loopWhileAlive : can the delegate runction in the given frequency and stop whe the registry is stopping 366 func (registry *ServiceRegistry) loopWhileAlive(frequency time.Duration, delegate func()) { 367 for { 368 if registry.stopping { 369 break 370 } 371 delegate() 372 time.Sleep(frequency) 373 } 374 } 375 376 func (registry *ServiceRegistry) filterMessages(handler func(message moleculer.Payload)) func(message moleculer.Payload) { 377 return func(message moleculer.Payload) { 378 if registry.stopping { 379 registry.logger.Warn("filterMessages() - registry is stopping. Discarding message: ", message) 380 return 381 } 382 if message.Get("sender").Exists() && message.Get("sender").String() == registry.localNode.GetID() { 383 registry.logger.Debug("filterMessages() - Same host message (sender == localNodeID). discarding... ", message) 384 return 385 } 386 handler(message) 387 } 388 } 389 390 func (registry *ServiceRegistry) heartbeatMessageReceived(message moleculer.Payload) { 391 heartbeat := message.RawMap() 392 succesful := registry.nodes.HeartBeat(heartbeat) 393 if !succesful { 394 sender := heartbeat["sender"].(string) 395 registry.transit.DiscoverNode(sender) 396 } 397 } 398 399 // disconnectMessageReceived handles when a disconnect msg is received. 400 // It remove all actions/events from the sender node from the local registry. 401 func (registry *ServiceRegistry) disconnectMessageReceived(message moleculer.Payload) { 402 sender := message.Get("sender").String() 403 node, exists := registry.nodes.findNode(sender) 404 registry.logger.Debug("disconnectMessageReceived() sender: ", sender, " exists: ", exists) 405 if exists { 406 registry.disconnectNode(node.GetID()) 407 } 408 } 409 410 func compatibility(info map[string]interface{}) map[string]interface{} { 411 _, exists := info["version"] 412 if !exists { 413 info["version"] = "" 414 } 415 return info 416 } 417 418 // remoteNodeInfoReceived process the remote node info message and add to local registry. 419 func (registry *ServiceRegistry) remoteNodeInfoReceived(message moleculer.Payload) { 420 registry.nodeReceivedMutex.Lock() 421 defer registry.nodeReceivedMutex.Unlock() 422 423 msgMap := message.RawMap() 424 nodeID := message.Get("sender").String() 425 services := message.Get("services").MapArray() 426 exists, reconnected, removedServices := registry.nodes.Info(msgMap) 427 428 for _, serviceInfo := range services { 429 serviceInfo = compatibility(serviceInfo) 430 svc, newService, updatedActions, newActions, deletedActions, updatedEvents, newEvents, deletedEvents := registry.services.updateRemote(nodeID, serviceInfo) 431 432 for _, newAction := range newActions { 433 serviceAction := service.CreateServiceAction( 434 serviceInfo["name"].(string), 435 newAction.Name(), 436 nil, 437 moleculer.ObjectSchema{nil}) 438 registry.actions.Add(serviceAction, svc, false) 439 } 440 441 for _, updates := range updatedActions { 442 fullname := updates["name"].(string) 443 registry.actions.Update(nodeID, fullname, updates) 444 } 445 446 for _, deleted := range deletedActions { 447 fullname := deleted.FullName() 448 registry.actions.Remove(nodeID, fullname) 449 } 450 451 for _, newEvent := range newEvents { 452 serviceEvent := service.CreateServiceEvent( 453 newEvent.Name(), 454 serviceInfo["name"].(string), 455 newEvent.Group(), 456 newEvent.Handler()) 457 registry.events.Add(serviceEvent, svc, false) 458 } 459 460 for _, updates := range updatedEvents { 461 name := updates["name"].(string) 462 registry.events.Update(nodeID, name, updates) 463 } 464 465 for _, deleted := range deletedEvents { 466 name := deleted.Name() 467 registry.events.Remove(nodeID, name) 468 } 469 470 if newService { 471 registry.logger.Infof("Registry - remote %s service is registered.", svc.FullName()) 472 473 registry.broker.Bus().EmitAsync( 474 "$registry.service.added", 475 []interface{}{svc.Summary()}) 476 } 477 478 for _, removedService := range removedServices { 479 name := removedService["name"].(string) 480 registry.services.Remove(nodeID, name) 481 } 482 } 483 484 var neighbours int64 485 if message.Get("neighbours").Exists() { 486 neighbours = message.Get("neighbours").Int64() 487 } 488 489 eventParam := []interface{}{nodeID, neighbours} 490 eventName := "$node.connected" 491 if exists { 492 eventName = "$node.updated" 493 } else if reconnected { 494 eventName = "$node.reconnected" 495 } 496 registry.broker.Bus().EmitAsync(eventName, eventParam) 497 } 498 499 // subscribeInternalEvent subscribe event listeners for internal events (e.g. $node.disconnected) using the localBus. 500 func (registry *ServiceRegistry) subscribeInternalEvent(event service.Event) { 501 registry.broker.Bus().On(event.Name(), func(data ...interface{}) { 502 params := payload.New(nil) 503 if len(data) > 0 { 504 params = payload.New(data[0]) 505 } 506 brokerContext := registry.broker.BrokerContext() 507 eventContext := brokerContext.ChildEventContext(event.Name(), params, nil, false) 508 event.Handler()(eventContext.(moleculer.Context), params) 509 }) 510 } 511 512 // AddLocalService : add a local service to the registry 513 // it will create endpoints for all service actions. 514 func (registry *ServiceRegistry) AddLocalService(service *service.Service) { 515 if registry.services.Find(service.Name(), service.Version(), registry.localNode.GetID()) { 516 registry.logger.Trace("registry - AddLocalService() - Service already registered, will ignore.. service fullName: ", service.FullName()) 517 return 518 } 519 520 registry.services.Add(service) 521 522 actions := service.Actions() 523 events := service.Events() 524 525 for _, action := range actions { 526 registry.actions.Add(action, service, true) 527 } 528 for _, event := range events { 529 if strings.Index(event.Name(), "$") == 0 { 530 registry.subscribeInternalEvent(event) 531 } else { 532 registry.events.Add(event, service, true) 533 } 534 } 535 registry.localNode.Publish(service.AsMap()) 536 registry.logger.Debug("Registry published local service: ", service.FullName(), " # actions: ", len(actions), " # events: ", len(events), " nodeID: ", service.NodeID()) 537 registry.notifyServiceAdded(service.Summary()) 538 } 539 540 // notifyServiceAdded notify when a service is added to the registry. 541 func (registry *ServiceRegistry) notifyServiceAdded(svc map[string]string) { 542 if registry.broker.IsStarted() { 543 registry.broker.Bus().EmitAsync( 544 "$registry.service.added", 545 []interface{}{svc}) 546 } else { 547 registry.broker.Bus().Once("$broker.started", func(...interface{}) { 548 registry.broker.Bus().EmitAsync( 549 "$registry.service.added", 550 []interface{}{svc}) 551 }) 552 } 553 } 554 555 // nextAction it will find and return the next action to be invoked. 556 // If multiple nodes that contain this action are found it will use the strategy to decide which one to use. 557 func (registry *ServiceRegistry) nextAction(actionName string, strategy strategy.Strategy, opts ...moleculer.Options) *ActionEntry { 558 if len(opts) > 0 && opts[0].NodeID != "" { 559 return registry.actions.NextFromNode(actionName, opts[0].NodeID) 560 } 561 return registry.actions.Next(actionName, strategy) 562 } 563 564 func (registry *ServiceRegistry) KnownEventListeners(addNode bool) []string { 565 events := registry.events.list() 566 result := make([]string, len(events)) 567 for index, event := range events { 568 if addNode { 569 result[index] = fmt.Sprint(event.targetNodeID, ".", event.event.ServiceName(), ".", event.event.Name()) 570 } else { 571 result[index] = fmt.Sprint(event.event.ServiceName(), ".", event.event.Name()) 572 } 573 574 } 575 sort.Strings(result) 576 return result 577 } 578 579 func (registry *ServiceRegistry) KnownNodes() []string { 580 nodes := registry.nodes.list() 581 result := make([]string, len(nodes)) 582 for index, node := range nodes { 583 result[index] = node.GetID() 584 } 585 sort.Strings(result) 586 return result 587 }