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

     1  package nats
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/moleculer-go/moleculer"
    10  	"github.com/moleculer-go/moleculer/serializer"
    11  	"github.com/moleculer-go/moleculer/transit"
    12  	"github.com/nats-io/go-nats"
    13  	log "github.com/sirupsen/logrus"
    14  )
    15  
    16  type NatsTransporter struct {
    17  	prefix        string
    18  	opts          *nats.Options
    19  	conn          *nats.Conn
    20  	logger        *log.Entry
    21  	serializer    serializer.Serializer
    22  	subscriptions []*nats.Subscription
    23  }
    24  
    25  type NATSOptions struct {
    26  	URL         string
    27  	Name        string
    28  	Logger      *log.Entry
    29  	Serializer  serializer.Serializer
    30  	ValidateMsg transit.ValidateMsgFunc
    31  
    32  	AllowReconnect bool
    33  	ReconnectWait  time.Duration
    34  	MaxReconnect   int
    35  }
    36  
    37  func natsOptions(options NATSOptions) *nats.Options {
    38  	opts := nats.GetDefaultOptions()
    39  	opts.Name = options.Name
    40  	opts.Url = options.URL
    41  	opts.AllowReconnect = options.AllowReconnect
    42  	if options.ReconnectWait != 0 {
    43  		opts.ReconnectWait = options.ReconnectWait
    44  	}
    45  	if options.MaxReconnect != 0 {
    46  		opts.MaxReconnect = options.MaxReconnect
    47  	}
    48  	return &opts
    49  }
    50  
    51  func CreateNatsTransporter(options NATSOptions) transit.Transport {
    52  	return &NatsTransporter{
    53  		opts:          natsOptions(options),
    54  		logger:        options.Logger,
    55  		serializer:    options.Serializer,
    56  		subscriptions: []*nats.Subscription{},
    57  	}
    58  }
    59  
    60  func (t *NatsTransporter) Connect() chan error {
    61  	endChan := make(chan error)
    62  	go func() {
    63  		t.logger.Debug("NATS Connect() - url: ", t.opts.Url, " Name: ", t.opts.Name)
    64  		conn, err := t.opts.Connect()
    65  		if err != nil {
    66  			t.logger.Error("NATS Connect() - Error: ", err, " url: ", t.opts.Url, " Name: ", t.opts.Name)
    67  			endChan <- errors.New(fmt.Sprint("Error connection to NATS. error: ", err, " url: ", t.opts.Url))
    68  			return
    69  		}
    70  
    71  		t.logger.Info("Connected to ", t.opts.Url)
    72  		t.conn = conn
    73  		endChan <- nil
    74  	}()
    75  	return endChan
    76  }
    77  
    78  func (t *NatsTransporter) Disconnect() chan error {
    79  	endChan := make(chan error)
    80  	go func() {
    81  		if t.conn == nil {
    82  			endChan <- nil
    83  			return
    84  		}
    85  		for _, sub := range t.subscriptions {
    86  			if err := sub.Unsubscribe(); err != nil {
    87  				t.logger.Error(err)
    88  			}
    89  		}
    90  		t.conn.Close()
    91  		t.conn = nil
    92  		endChan <- nil
    93  	}()
    94  	return endChan
    95  }
    96  
    97  func (t *NatsTransporter) topicName(command string, nodeID string) string {
    98  	parts := []string{t.prefix, command}
    99  	if nodeID != "" {
   100  		parts = append(parts, nodeID)
   101  	}
   102  	return strings.Join(parts, ".")
   103  }
   104  
   105  func (t *NatsTransporter) Subscribe(command, nodeID string, handler transit.TransportHandler) {
   106  	if t.conn == nil {
   107  		msg := fmt.Sprint("nats.Subscribe() No connection :( -> command: ", command, " nodeID: ", nodeID)
   108  		t.logger.Warn(msg)
   109  		panic(errors.New(msg))
   110  	}
   111  
   112  	topic := t.topicName(command, nodeID)
   113  
   114  	sub, err := t.conn.Subscribe(topic, func(msg *nats.Msg) {
   115  		payload := t.serializer.BytesToPayload(&msg.Data)
   116  		t.logger.Debug(fmt.Sprintf("Incoming %s packet from '%s'", topic, payload.Get("sender").String()))
   117  		handler(payload)
   118  	})
   119  	if err != nil {
   120  		t.logger.Error("Cannot subscribe: ", topic, " error: ", err)
   121  		return
   122  	}
   123  	t.subscriptions = append(t.subscriptions, sub)
   124  }
   125  
   126  func (t *NatsTransporter) Publish(command, nodeID string, message moleculer.Payload) {
   127  	if t.conn == nil {
   128  		msg := fmt.Sprint("nats.Publish() No connection :( -> command: ", command, " nodeID: ", nodeID)
   129  		t.logger.Warn(msg)
   130  		panic(errors.New(msg))
   131  	}
   132  
   133  	topic := t.topicName(command, nodeID)
   134  	t.logger.Debug("nats.Publish() command: ", command, " topic: ", topic, " nodeID: ", nodeID)
   135  	t.logger.Trace("message: \n", message, "\n - end")
   136  	err := t.conn.Publish(topic, t.serializer.PayloadToBytes(message))
   137  	if err != nil {
   138  		t.logger.Error("Error on publish: error: ", err, " command: ", command, " topic: ", topic)
   139  		panic(err)
   140  	}
   141  }
   142  
   143  func (t *NatsTransporter) SetPrefix(prefix string) {
   144  	t.prefix = prefix
   145  }
   146  
   147  func (t *NatsTransporter) SetNodeID(nodeID string) {
   148  }
   149  
   150  func (t *NatsTransporter) SetSerializer(serializer serializer.Serializer) {
   151  	// Ignored while transporter initialized in pubsub function
   152  }