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  }