github.com/moleculer-go/moleculer@v0.3.3/transit/pubsub/pubsub.go (about) 1 package pubsub 2 3 import ( 4 "errors" 5 "fmt" 6 "math" 7 "os" 8 "strings" 9 "sync" 10 "time" 11 12 "github.com/moleculer-go/moleculer/version" 13 14 "github.com/moleculer-go/moleculer/payload" 15 16 "github.com/moleculer-go/moleculer/context" 17 "github.com/moleculer-go/moleculer/transit" 18 "github.com/moleculer-go/moleculer/transit/kafka" 19 "github.com/moleculer-go/moleculer/transit/memory" 20 "github.com/moleculer-go/moleculer/transit/nats" 21 "github.com/moleculer-go/moleculer/util" 22 23 "github.com/moleculer-go/moleculer" 24 "github.com/moleculer-go/moleculer/serializer" 25 log "github.com/sirupsen/logrus" 26 ) 27 28 // PubSub is a transit implementation. 29 type PubSub struct { 30 logger *log.Entry 31 transport transit.Transport 32 broker *moleculer.BrokerDelegates 33 isConnected bool 34 pendingRequests map[string]pendingRequest 35 pendingRequestsMutex *sync.Mutex 36 serializer serializer.Serializer 37 38 knownNeighbours map[string]int64 39 neighboursTimeout time.Duration 40 neighboursMutex *sync.Mutex 41 brokerStarted bool 42 } 43 44 const DATATYPE_UNDEFINED = 0 45 const DATATYPE_NULL = 1 46 const DATATYPE_JSON = 2 47 const DATATYPE_BUFFER = 3 48 49 func (pubsub *PubSub) onServiceAdded(values ...interface{}) { 50 if pubsub.isConnected && pubsub.brokerStarted { 51 localNodeID := pubsub.broker.LocalNode().GetID() 52 53 // Checking that was added local service 54 isLocalServiceAdded := false 55 for _, value := range values { 56 if value.(map[string]string)["nodeID"] == localNodeID { 57 isLocalServiceAdded = true 58 break 59 } 60 } 61 62 if isLocalServiceAdded { 63 pubsub.broker.LocalNode().IncreaseSequence() 64 pubsub.broadcastNodeInfo("") 65 } 66 } 67 } 68 69 func (pubsub *PubSub) onBrokerStarted(values ...interface{}) { 70 if pubsub.isConnected { 71 pubsub.broadcastNodeInfo("") 72 pubsub.brokerStarted = true 73 } 74 } 75 76 func Create(broker *moleculer.BrokerDelegates) transit.Transit { 77 pendingRequests := make(map[string]pendingRequest) 78 knownNeighbours := make(map[string]int64) 79 transitImpl := PubSub{ 80 broker: broker, 81 isConnected: false, 82 pendingRequests: pendingRequests, 83 logger: broker.Logger("Transit", ""), 84 serializer: serializer.New(broker), 85 neighboursTimeout: broker.Config.NeighboursCheckTimeout, 86 knownNeighbours: knownNeighbours, 87 neighboursMutex: &sync.Mutex{}, 88 pendingRequestsMutex: &sync.Mutex{}, 89 } 90 91 broker.Bus().On("$node.disconnected", transitImpl.onNodeDisconnected) 92 broker.Bus().On("$node.connected", transitImpl.onNodeConnected) 93 broker.Bus().On("$broker.started", transitImpl.onBrokerStarted) 94 broker.Bus().On("$registry.service.added", transitImpl.onServiceAdded) 95 96 return &transitImpl 97 } 98 99 func (pubsub *PubSub) pendingRequestsByNode(nodeId string) []pendingRequest { 100 list := []pendingRequest{} 101 for _, p := range pubsub.pendingRequests { 102 if p.context.TargetNodeID() == nodeId { 103 list = append(list, p) 104 } 105 } 106 return list 107 } 108 109 func (pubsub *PubSub) requestTimedOut(resultChan *chan moleculer.Payload, context moleculer.BrokerContext) func() { 110 pError := payload.New(errors.New("request timeout")) 111 return func() { 112 pubsub.logger.Debug("requestTimedOut() nodeID: ", context.TargetNodeID()) 113 pubsub.pendingRequestsMutex.Lock() 114 defer pubsub.pendingRequestsMutex.Unlock() 115 116 p, exists := pubsub.pendingRequests[context.ID()] 117 if exists { 118 (*p.resultChan) <- pError 119 p.timer.Stop() 120 delete(pubsub.pendingRequests, p.context.ID()) 121 } 122 } 123 } 124 125 func (pubsub *PubSub) onNodeDisconnected(values ...interface{}) { 126 pubsub.pendingRequestsMutex.Lock() 127 128 var nodeID string = values[0].(string) 129 pending := pubsub.pendingRequestsByNode(nodeID) 130 pubsub.logger.Debug("onNodeDisconnected() nodeID: ", nodeID, " pending: ", len(pending)) 131 if len(pending) > 0 { 132 pError := payload.New(fmt.Errorf("Node %s disconnected. The request was canceled.", nodeID)) 133 for _, p := range pending { 134 (*p.resultChan) <- pError 135 p.timer.Stop() 136 delete(pubsub.pendingRequests, p.context.ID()) 137 } 138 } 139 pubsub.pendingRequestsMutex.Unlock() 140 141 pubsub.neighboursMutex.Lock() 142 delete(pubsub.knownNeighbours, nodeID) 143 pubsub.neighboursMutex.Unlock() 144 } 145 146 func (pubsub *PubSub) onNodeConnected(values ...interface{}) { 147 nodeID := values[0].(string) 148 neighbours := values[1].(int64) 149 pubsub.logger.Debug("onNodeConnected() nodeID: ", nodeID, " neighbours: ", neighbours) 150 pubsub.neighboursMutex.Lock() 151 pubsub.knownNeighbours[nodeID] = neighbours 152 pubsub.neighboursMutex.Unlock() 153 } 154 155 func isNats(v string) bool { 156 return strings.Index(v, "nats://") > -1 157 } 158 159 func isKafka(v string) bool { 160 return strings.Contains(v, "kafka://") 161 } 162 163 // CreateTransport : based on config it will load the transporter 164 // for now is hard coded for NATS Streaming localhost 165 func (pubsub *PubSub) createTransport() transit.Transport { 166 var transport transit.Transport 167 if pubsub.broker.Config.TransporterFactory != nil { 168 pubsub.logger.Info("Transporter: Custom factory") 169 transport = pubsub.broker.Config.TransporterFactory().(transit.Transport) 170 } else if pubsub.broker.Config.Transporter == "STAN" { 171 pubsub.logger.Info("Transporter: NatsStreamingTransporter") 172 transport = pubsub.createStanTransporter() 173 } else if isNats(pubsub.broker.Config.Transporter) { 174 pubsub.logger.Info("Transporter: NatsTransporter") 175 transport = pubsub.createNatsTransporter() 176 } else if isKafka(pubsub.broker.Config.Transporter) { 177 pubsub.logger.Info("Transporter: KafkaTransporter") 178 transport = pubsub.createKafkaTransporter() 179 } else { 180 pubsub.logger.Info("Transporter: Memory") 181 transport = pubsub.createMemoryTransporter() 182 } 183 transport.SetPrefix(resolveNamespace(pubsub.broker.Config.Namespace)) 184 transport.SetNodeID(pubsub.broker.LocalNode().GetID()) 185 transport.SetSerializer(pubsub.serializer) 186 return transport 187 } 188 189 func resolveNamespace(namespace string) string { 190 if namespace != "" { 191 return "MOL-" + namespace 192 } 193 return "MOL" 194 } 195 196 func (pubsub *PubSub) createMemoryTransporter() transit.Transport { 197 pubsub.logger.Debug("createMemoryTransporter() ... ") 198 logger := pubsub.logger.WithField("transport", "memory") 199 mem := memory.Create(logger, &memory.SharedMemory{}) 200 return &mem 201 } 202 203 func (pubsub *PubSub) createKafkaTransporter() transit.Transport { 204 pubsub.logger.Debug("createKafkaTransporter()") 205 206 return kafka.CreateKafkaTransporter(kafka.KafkaOptions{ 207 Url: pubsub.broker.Config.Transporter, 208 Name: pubsub.broker.LocalNode().GetID(), 209 Logger: pubsub.logger.WithField("transport", "kafka"), 210 Serializer: pubsub.serializer, 211 }) 212 } 213 214 func (pubsub *PubSub) createNatsTransporter() transit.Transport { 215 pubsub.logger.Debug("createNatsTransporter()") 216 217 return nats.CreateNatsTransporter(nats.NATSOptions{ 218 URL: pubsub.broker.Config.Transporter, 219 Name: pubsub.broker.LocalNode().GetID(), 220 Logger: pubsub.logger.WithField("transport", "nats"), 221 Serializer: pubsub.serializer, 222 AllowReconnect: true, 223 ReconnectWait: time.Second * 2, 224 MaxReconnect: -1, 225 }) 226 } 227 228 func (pubsub *PubSub) createStanTransporter() transit.Transport { 229 broker := pubsub.broker 230 logger := broker.Logger("transport", "stan") 231 232 url := "stan://" + os.Getenv("STAN_HOST") + ":4222" 233 clusterID := "test-cluster" 234 localNodeID := broker.LocalNode().GetID() 235 clientID := strings.ReplaceAll(localNodeID, ".", "_") 236 237 options := nats.StanOptions{ 238 url, 239 clusterID, 240 clientID, 241 logger, 242 pubsub.serializer, 243 func(message moleculer.Payload) bool { 244 sender := message.Get("sender").String() 245 return sender != localNodeID 246 }, 247 } 248 249 stanTransporter := nats.CreateStanTransporter(options) 250 var transport transit.Transport = &stanTransporter 251 return transport 252 } 253 254 type pendingRequest struct { 255 context moleculer.BrokerContext 256 resultChan *chan moleculer.Payload 257 timer *time.Timer 258 } 259 260 func (pubsub *PubSub) checkMaxQueueSize() { 261 //TODO: check transit.js line 524 262 } 263 264 // waitForNeighbours this function will wait for neighbour nodes or timeout if the expected number is not received after a time out. 265 func (pubsub *PubSub) waitForNeighbours() bool { 266 if pubsub.broker.Config.DontWaitForNeighbours { 267 return true 268 } 269 start := time.Now() 270 for { 271 expected := pubsub.expectedNeighbours() 272 neighbours := pubsub.neighbours() 273 if expected <= neighbours && (expected > 0 || neighbours > 0) { 274 pubsub.logger.Debug("waitForNeighbours() - received info from all expected neighbours :) -> expected: ", expected) 275 return true 276 } 277 if time.Since(start) > pubsub.neighboursTimeout { 278 pubsub.logger.Warn("waitForNeighbours() - Time out ! did not receive info from all expected neighbours: ", expected, " INFOs received: ", neighbours) 279 return false 280 } 281 if !pubsub.isConnected { 282 return false 283 } 284 time.Sleep(pubsub.broker.Config.WaitForNeighboursInterval) 285 } 286 } 287 288 //DiscoverNodes will check if there are neighbours and return true if any are found ;). 289 func (pubsub *PubSub) DiscoverNodes() chan bool { 290 result := make(chan bool) 291 go func() { 292 pubsub.DiscoverNode("") 293 result <- pubsub.waitForNeighbours() 294 }() 295 return result 296 } 297 298 func (pubsub *PubSub) SendHeartbeat() { 299 node := pubsub.broker.LocalNode().ExportAsMap() 300 payload := map[string]interface{}{ 301 "sender": node["id"], 302 "cpu": node["cpu"], 303 "cpuSeq": node["cpuSeq"], 304 "ver": version.MoleculerProtocol(), 305 } 306 message, err := pubsub.serializer.MapToPayload(&payload) 307 if err == nil { 308 pubsub.transport.Publish("HEARTBEAT", "", message) 309 } 310 } 311 312 func (pubsub *PubSub) DiscoverNode(nodeID string) { 313 payload := map[string]interface{}{ 314 "sender": pubsub.broker.LocalNode().GetID(), 315 "ver": version.MoleculerProtocol(), 316 } 317 message, err := pubsub.serializer.MapToPayload(&payload) 318 if err == nil { 319 pubsub.transport.Publish("DISCOVER", nodeID, message) 320 } 321 } 322 323 // Emit emit an event to all services that listens to this event. 324 func (pubsub *PubSub) Emit(context moleculer.BrokerContext) { 325 targetNodeID := context.TargetNodeID() 326 payload := context.AsMap() 327 payload["sender"] = pubsub.broker.LocalNode().GetID() 328 payload["ver"] = version.MoleculerProtocol() 329 if context.Payload().Exists() { 330 payload["dataType"] = DATATYPE_JSON 331 } else { 332 payload["dataType"] = DATATYPE_NULL 333 } 334 335 pubsub.logger.Trace("Emit() targetNodeID: ", targetNodeID, " payload: ", payload) 336 337 message, err := pubsub.serializer.MapToPayload(&payload) 338 if err != nil { 339 pubsub.logger.Error("Emit() Error serializing the payload: ", payload, " error: ", err) 340 panic(fmt.Errorf("Error trying to serialize the payload. Likely issues with the action params. Error: %s", err)) 341 } 342 pubsub.transport.Publish("EVENT", targetNodeID, message) 343 } 344 345 func (pubsub *PubSub) Request(context moleculer.BrokerContext) chan moleculer.Payload { 346 pubsub.checkMaxQueueSize() 347 348 resultChan := make(chan moleculer.Payload) 349 350 targetNodeID := context.TargetNodeID() 351 payload := context.AsMap() 352 payload["sender"] = pubsub.broker.LocalNode().GetID() 353 payload["ver"] = version.MoleculerProtocol() 354 if context.Payload().Exists() { 355 payload["paramsType"] = DATATYPE_JSON 356 } else { 357 payload["paramsType"] = DATATYPE_NULL 358 } 359 360 pubsub.logger.Trace("Request() targetNodeID: ", targetNodeID, " payload: ", payload) 361 362 message, err := pubsub.serializer.MapToPayload(&payload) 363 if err != nil { 364 pubsub.logger.Error("Request() Error serializing the payload: ", payload, " error: ", err) 365 panic(fmt.Errorf("Error trying to serialize the payload. Likely issues with the action params. Error: %s", err)) 366 } 367 368 pubsub.pendingRequestsMutex.Lock() 369 pubsub.logger.Debug("Request() pending request id: ", context.ID(), " targetNodeId: ", context.TargetNodeID()) 370 pubsub.pendingRequests[context.ID()] = pendingRequest{ 371 context, 372 &resultChan, 373 374 time.AfterFunc( 375 pubsub.broker.Config.RequestTimeout, 376 pubsub.requestTimedOut(&resultChan, context)), 377 } 378 pubsub.pendingRequestsMutex.Unlock() 379 380 pubsub.transport.Publish("REQ", targetNodeID, message) 381 return resultChan 382 } 383 384 // validateVersion check that version of the message is correct. 385 func (pubsub *PubSub) validate(handler func(message moleculer.Payload)) transit.TransportHandler { 386 return func(msg moleculer.Payload) { 387 valid := pubsub.validateVersion(msg) && !pubsub.sameHost(msg) 388 if valid { 389 handler(msg) 390 } else { 391 pubsub.logger.Trace("Discarding invalid msg -> ", msg.Value()) 392 } 393 } 394 } 395 396 func (pubsub *PubSub) sameHost(msg moleculer.Payload) bool { 397 sender := msg.Get("sender").String() 398 localNodeID := pubsub.broker.LocalNode().GetID() 399 return sender == localNodeID 400 } 401 402 // validateVersion check that version of the message is correct. 403 func (pubsub *PubSub) validateVersion(msg moleculer.Payload) bool { 404 msgVersion := msg.Get("ver").String() 405 if msgVersion == version.MoleculerProtocol() { 406 return true 407 } else { 408 pubsub.logger.Error("Discarding msg - wronging version: ", msgVersion, " expected: ", version.MoleculerProtocol(), " msg: ", msg) 409 return false 410 } 411 } 412 413 // reponseHandler responsible for whem a reponse arrives form a remote node. 414 func (pubsub *PubSub) reponseHandler() transit.TransportHandler { 415 return func(message moleculer.Payload) { 416 pubsub.pendingRequestsMutex.Lock() 417 defer pubsub.pendingRequestsMutex.Unlock() 418 419 id := message.Get("id").String() 420 sender := message.Get("sender").String() 421 pubsub.logger.Debug("reponseHandler() - response arrived from nodeID: ", sender, " context id: ", id) 422 423 request, exists := pubsub.pendingRequests[id] 424 425 if !exists { 426 pubsub.logger.Debug("reponseHandler() - discarding response -> request does not exist for id: ", id, " - message: ", message.Value()) 427 return 428 } 429 if request.resultChan == nil { 430 pubsub.logger.Debug("reponseHandler() - discarding response -> request.resultChan is nil! - message: ", message.Value(), " pending context: ", request.context) 431 return 432 } 433 434 request.timer.Stop() 435 defer delete(pubsub.pendingRequests, id) 436 var result moleculer.Payload 437 if message.Get("success").Bool() { 438 result = message.Get("data") 439 } else { 440 result = pubsub.parseError(message) 441 } 442 443 pubsub.logger.Trace("reponseHandler() id: ", id, " result: ", result) 444 (*request.resultChan) <- result 445 } 446 } 447 448 func (pubsub *PubSub) parseError(message moleculer.Payload) moleculer.Payload { 449 if pubsub.isMoleculerJSError(message) { 450 return payload.New(pubsub.moleculerJSError(message)) 451 } 452 return payload.New(errors.New(message.Get("error").String())) 453 } 454 455 func (pubsub *PubSub) isMoleculerJSError(message moleculer.Payload) bool { 456 return message.Get("error").Get("message").Exists() 457 } 458 459 func (pubsub *PubSub) moleculerJSError(message moleculer.Payload) error { 460 msg := message.Get("error").Get("message").String() 461 if message.Get("error").Get("stack").Exists() { 462 pubsub.logger.Error(message.Get("error").Get("stack").Value()) 463 } 464 return errors.New(msg) 465 } 466 467 func (pubsub *PubSub) sendResponse(context moleculer.BrokerContext, response moleculer.Payload) { 468 targetNodeID := context.TargetNodeID() 469 470 if targetNodeID == "" { 471 panic(errors.New("sendResponse() targetNodeID is required !")) 472 } 473 474 pubsub.logger.Tracef("sendResponse() reponse type: %T ", response) 475 476 values := make(map[string]interface{}) 477 values["sender"] = pubsub.broker.LocalNode().GetID() 478 values["ver"] = version.MoleculerProtocol() 479 values["id"] = context.ID() 480 values["meta"] = context.Meta() 481 482 if response.Exists() { 483 values["dataType"] = DATATYPE_JSON 484 } else { 485 values["dataType"] = DATATYPE_NULL 486 } 487 488 if response.IsError() { 489 var errMap map[string]string 490 actionError, isActionError := response.Value().(ActionError) 491 if isActionError { 492 errMap = map[string]string{ 493 "message": actionError.Error(), 494 "stack": actionError.Stack(), 495 "name": "Error", 496 } 497 } else { 498 errMap = map[string]string{ 499 "message": response.String(), 500 "name": "Error", 501 } 502 } 503 values["success"] = false 504 values["error"] = errMap 505 } else { 506 values["success"] = true 507 values["data"] = response.Value() 508 } 509 510 message, err := pubsub.serializer.MapToPayload(&values) 511 if err != nil { 512 pubsub.logger.Error("sendResponse() Erro serializing the values: ", values, " error: ", err) 513 panic(err) 514 } 515 516 pubsub.logger.Trace("sendResponse() targetNodeID: ", targetNodeID, " values: ", values, " message: ", message) 517 518 pubsub.transport.Publish("RES", targetNodeID, message) 519 } 520 521 type ActionError interface { 522 Error() string 523 Stack() string 524 } 525 526 func parseParamsType(value moleculer.Payload) string { 527 if !value.Exists() { 528 return "1" //default : null 529 } 530 return value.String() 531 } 532 533 // requestHandler : handles when a request arrives on this node. 534 // 1: create a moleculer.Context from the message, the moleculer.Context contains the target action 535 // 2: invoke the action 536 // 3: send a response 537 func (pubsub *PubSub) requestHandler() transit.TransportHandler { 538 return func(message moleculer.Payload) { 539 paramsType := parseParamsType(message.Get("paramsType")) 540 if paramsType != "1" && paramsType != "2" { 541 errMsg := "Expecting paramsType == 2 (JSON) or 1 (Null) - received: " + paramsType 542 pubsub.logger.Error(errMsg) 543 //currently there is only one serializer implementation. 544 //once more serializers are added, pubsub.serializer must change and be dinamic based on paramsType 545 pubsub.sendResponse(context.ActionContext(pubsub.broker, nil), payload.Error(errMsg)) 546 return 547 } 548 values := pubsub.serializer.PayloadToContextMap(message) 549 context := context.ActionContext(pubsub.broker, values) 550 result := <-pubsub.broker.ActionDelegate(context) 551 pubsub.sendResponse(context, result) 552 } 553 } 554 555 //eventHandler handles when a event msg is sent to this broker 556 func (pubsub *PubSub) eventHandler() transit.TransportHandler { 557 return func(message moleculer.Payload) { 558 values := pubsub.serializer.PayloadToContextMap(message) 559 context := context.EventContext(pubsub.broker, values) 560 pubsub.broker.HandleRemoteEvent(context) 561 } 562 } 563 564 // expectedNeighbours calculate the expected number of neighbours 565 func (pubsub *PubSub) expectedNeighbours() int64 { 566 neighbours := pubsub.neighbours() 567 if neighbours == 0 { 568 return 0 569 } 570 571 var total int64 572 pubsub.neighboursMutex.Lock() 573 for _, value := range pubsub.knownNeighbours { 574 total = total + value 575 } 576 pubsub.neighboursMutex.Unlock() 577 return total / neighbours 578 } 579 580 // neighbours return the total number of known neighbours. 581 func (pubsub *PubSub) neighbours() int64 { 582 return int64(len(pubsub.knownNeighbours)) 583 } 584 585 func configToMap(config moleculer.Config) map[string]string { 586 m := make(map[string]string) 587 m["logLevel"] = config.LogLevel 588 m["transporter"] = config.Transporter 589 m["namespace"] = config.Namespace 590 m["requestTimeout"] = config.RequestTimeout.String() 591 return m 592 } 593 594 // broadcastNodeInfo send the local node info to the target node, if empty to all nodes. 595 func (pubsub *PubSub) broadcastNodeInfo(targetNodeID string) { 596 payload := pubsub.broker.LocalNode().ExportAsMap() 597 payload["sender"] = payload["id"] 598 payload["neighbours"] = pubsub.neighbours() 599 payload["ver"] = version.MoleculerProtocol() 600 payload["config"] = configToMap(pubsub.broker.Config) 601 payload["instanceID"] = pubsub.broker.InstanceID() 602 603 message, _ := pubsub.serializer.MapToPayload(&payload) 604 pubsub.transport.Publish("INFO", targetNodeID, message) 605 } 606 607 func (pubsub *PubSub) discoverHandler() transit.TransportHandler { 608 return func(message moleculer.Payload) { 609 sender := message.Get("sender").String() 610 if pubsub.brokerStarted { 611 pubsub.broadcastNodeInfo(sender) 612 } else { 613 pubsub.broker.Bus().Once("$broker.started", func(...interface{}) { 614 pubsub.broadcastNodeInfo(sender) 615 }) 616 } 617 } 618 } 619 620 func (pubsub *PubSub) emitRegistryEvent(command string) transit.TransportHandler { 621 return func(message moleculer.Payload) { 622 pubsub.logger.Trace("emitRegistryEvent() command: ", command, " message: ", message) 623 pubsub.broker.Bus().EmitAsync("$registry.transit.message", []interface{}{command, message}) 624 } 625 } 626 627 func (pubsub *PubSub) SendPing() { 628 ping := make(map[string]interface{}) 629 sender := pubsub.broker.LocalNode().GetID() 630 ping["sender"] = sender 631 ping["ver"] = version.MoleculerProtocol() 632 ping["time"] = time.Now().Unix() 633 ping["id"] = util.RandomString(12) 634 pingMessage, _ := pubsub.serializer.MapToPayload(&ping) 635 pubsub.transport.Publish("PING", sender, pingMessage) 636 637 } 638 639 func (pubsub *PubSub) pingHandler() transit.TransportHandler { 640 return func(message moleculer.Payload) { 641 pong := make(map[string]interface{}) 642 sender := message.Get("sender").String() 643 pong["sender"] = sender 644 pong["ver"] = version.MoleculerProtocol() 645 pong["time"] = message.Get("time").Int() 646 pong["arrived"] = time.Now().Unix() 647 pong["id"] = util.RandomString(12) 648 649 pongMessage, _ := pubsub.serializer.MapToPayload(&pong) 650 pubsub.transport.Publish("PONG", sender, pongMessage) 651 } 652 } 653 654 func (pubsub *PubSub) pongHandler() transit.TransportHandler { 655 return func(message moleculer.Payload) { 656 now := time.Now().Unix() 657 elapsed := now - message.Get("time").Int64() 658 arrived := message.Get("arrived").Int64() 659 timeDiff := math.Round( 660 float64(now) - float64(arrived) - float64(elapsed)/2) 661 662 mapValue := make(map[string]interface{}) 663 mapValue["nodeID"] = message.Get("sender").String() 664 mapValue["elapsedTime"] = elapsed 665 mapValue["timeDiff"] = timeDiff 666 mapValue["id"] = message.Get("id").String() 667 668 pubsub.broker.Bus().EmitAsync("$node.pong", []interface{}{mapValue}) 669 } 670 } 671 672 func (pubsub *PubSub) subscribe() { 673 nodeID := pubsub.broker.LocalNode().GetID() 674 pubsub.transport.Subscribe("RES", nodeID, pubsub.validate(pubsub.reponseHandler())) 675 676 pubsub.transport.Subscribe("REQ", nodeID, pubsub.validate(pubsub.requestHandler())) 677 //pubsub.transport.Subscribe("REQB", nodeID, pubsub.requestHandler()) 678 pubsub.transport.Subscribe("EVENT", nodeID, pubsub.validate(pubsub.eventHandler())) 679 680 pubsub.transport.Subscribe("HEARTBEAT", "", pubsub.validate(pubsub.emitRegistryEvent("HEARTBEAT"))) 681 pubsub.transport.Subscribe("DISCONNECT", "", pubsub.validate(pubsub.emitRegistryEvent("DISCONNECT"))) 682 pubsub.transport.Subscribe("INFO", "", pubsub.validate(pubsub.emitRegistryEvent("INFO"))) 683 pubsub.transport.Subscribe("INFO", nodeID, pubsub.validate(pubsub.emitRegistryEvent("INFO"))) 684 pubsub.transport.Subscribe("DISCOVER", nodeID, pubsub.validate(pubsub.discoverHandler())) 685 pubsub.transport.Subscribe("DISCOVER", "", pubsub.validate(pubsub.discoverHandler())) 686 pubsub.transport.Subscribe("PING", nodeID, pubsub.validate(pubsub.pingHandler())) 687 pubsub.transport.Subscribe("PONG", nodeID, pubsub.validate(pubsub.pongHandler())) 688 689 } 690 691 // sendDisconnect broadcast a DISCONNECT pkt to all nodes informing this one is stopping. 692 func (pubsub *PubSub) sendDisconnect() { 693 payload := make(map[string]interface{}) 694 payload["sender"] = pubsub.broker.LocalNode().GetID() 695 payload["ver"] = version.MoleculerProtocol() 696 msg, _ := pubsub.serializer.MapToPayload(&payload) 697 pubsub.transport.Publish("DISCONNECT", "", msg) 698 } 699 700 // Disconnect : disconnect the transit's transporter. 701 func (pubsub *PubSub) Disconnect() chan error { 702 endChan := make(chan error) 703 if !pubsub.isConnected { 704 endChan <- nil 705 return endChan 706 } 707 pubsub.logger.Info("PubSub - Disconnecting transport...") 708 pubsub.sendDisconnect() 709 pubsub.isConnected = false 710 return pubsub.transport.Disconnect() 711 } 712 713 // Connect : connect the transit with the transporter, subscribe to all events and start publishing its node info 714 func (pubsub *PubSub) Connect() chan error { 715 endChan := make(chan error) 716 if pubsub.isConnected { 717 endChan <- nil 718 return endChan 719 } 720 pubsub.logger.Debug("PubSub - Connecting transport...") 721 pubsub.transport = pubsub.createTransport() 722 go func() { 723 err := <-pubsub.transport.Connect() 724 if err == nil { 725 pubsub.isConnected = true 726 pubsub.logger.Debug("PubSub - Transport Connected!") 727 728 pubsub.subscribe() 729 730 } else { 731 pubsub.logger.Debug("PubSub - Error connecting transport - error: ", err) 732 } 733 endChan <- err 734 }() 735 return endChan 736 } 737 738 func (pubsub *PubSub) Ready() { 739 740 }