github.com/argoproj/argo-events@v1.9.1/eventsources/sources/nsq/start.go (about)

     1  /*
     2  Copyright 2020 BlackRock, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package nsq
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"strconv"
    24  	"time"
    25  
    26  	"github.com/nsqio/go-nsq"
    27  	"go.uber.org/zap"
    28  
    29  	"github.com/argoproj/argo-events/common"
    30  	"github.com/argoproj/argo-events/common/logging"
    31  	eventsourcecommon "github.com/argoproj/argo-events/eventsources/common"
    32  	"github.com/argoproj/argo-events/eventsources/sources"
    33  	metrics "github.com/argoproj/argo-events/metrics"
    34  	apicommon "github.com/argoproj/argo-events/pkg/apis/common"
    35  	"github.com/argoproj/argo-events/pkg/apis/events"
    36  	"github.com/argoproj/argo-events/pkg/apis/eventsource/v1alpha1"
    37  )
    38  
    39  // EventListener implements Eventing for the NSQ event source
    40  type EventListener struct {
    41  	EventSourceName string
    42  	EventName       string
    43  	NSQEventSource  v1alpha1.NSQEventSource
    44  	Metrics         *metrics.Metrics
    45  }
    46  
    47  // GetEventSourceName returns name of event source
    48  func (el *EventListener) GetEventSourceName() string {
    49  	return el.EventSourceName
    50  }
    51  
    52  // GetEventName returns name of event
    53  func (el *EventListener) GetEventName() string {
    54  	return el.EventName
    55  }
    56  
    57  // GetEventSourceType return type of event server
    58  func (el *EventListener) GetEventSourceType() apicommon.EventSourceType {
    59  	return apicommon.NSQEvent
    60  }
    61  
    62  type messageHandler struct {
    63  	eventSourceName string
    64  	eventName       string
    65  	metrics         *metrics.Metrics
    66  	dispatch        func([]byte, ...eventsourcecommon.Option) error
    67  	logger          *zap.SugaredLogger
    68  	isJSON          bool
    69  	metadata        map[string]string
    70  }
    71  
    72  // StartListening listens NSQ events
    73  func (el *EventListener) StartListening(ctx context.Context, dispatch func([]byte, ...eventsourcecommon.Option) error) error {
    74  	log := logging.FromContext(ctx).
    75  		With(logging.LabelEventSourceType, el.GetEventSourceType(), logging.LabelEventName, el.GetEventName())
    76  	log.Info("started processing the NSQ event source...")
    77  	defer sources.Recover(el.GetEventName())
    78  
    79  	nsqEventSource := &el.NSQEventSource
    80  
    81  	// Instantiate a consumer that will subscribe to the provided channel.
    82  	log.Info("creating a NSQ consumer")
    83  	var consumer *nsq.Consumer
    84  	config := nsq.NewConfig()
    85  
    86  	if nsqEventSource.TLS != nil {
    87  		tlsConfig, err := common.GetTLSConfig(nsqEventSource.TLS)
    88  		if err != nil {
    89  			return fmt.Errorf("failed to get the tls configuration, %w", err)
    90  		}
    91  		config.TlsConfig = tlsConfig
    92  		config.TlsV1 = true
    93  	}
    94  
    95  	if err := common.DoWithRetry(nsqEventSource.ConnectionBackoff, func() error {
    96  		var err error
    97  		if consumer, err = nsq.NewConsumer(nsqEventSource.Topic, nsqEventSource.Channel, config); err != nil {
    98  			return err
    99  		}
   100  		return nil
   101  	}); err != nil {
   102  		return fmt.Errorf("failed to create a new consumer for topic %s and channel %s for event source %s, %w", nsqEventSource.Topic, nsqEventSource.Channel, el.GetEventName(), err)
   103  	}
   104  
   105  	if nsqEventSource.JSONBody {
   106  		log.Info("assuming all events have a json body...")
   107  	}
   108  
   109  	consumer.AddHandler(&messageHandler{eventSourceName: el.EventSourceName, eventName: el.EventName, dispatch: dispatch, logger: log, isJSON: nsqEventSource.JSONBody, metadata: nsqEventSource.Metadata, metrics: el.Metrics})
   110  
   111  	if err := consumer.ConnectToNSQLookupd(nsqEventSource.HostAddress); err != nil {
   112  		return fmt.Errorf("lookup failed for host %s for event source %s, %w", nsqEventSource.HostAddress, el.GetEventName(), err)
   113  	}
   114  
   115  	<-ctx.Done()
   116  	log.Info("event source has stopped")
   117  	consumer.Stop()
   118  	return nil
   119  }
   120  
   121  // HandleMessage implements the Handler interface.
   122  func (h *messageHandler) HandleMessage(m *nsq.Message) error {
   123  	defer func(start time.Time) {
   124  		h.metrics.EventProcessingDuration(h.eventSourceName, h.eventName, float64(time.Since(start)/time.Millisecond))
   125  	}(time.Now())
   126  
   127  	h.logger.Info("received a message")
   128  
   129  	eventData := &events.NSQEventData{
   130  		Body:        m.Body,
   131  		Timestamp:   strconv.Itoa(int(m.Timestamp)),
   132  		NSQDAddress: m.NSQDAddress,
   133  		Metadata:    h.metadata,
   134  	}
   135  	if h.isJSON {
   136  		eventData.Body = (*json.RawMessage)(&m.Body)
   137  	}
   138  
   139  	eventBody, err := json.Marshal(eventData)
   140  	if err != nil {
   141  		h.logger.Errorw("failed to marshal the event data. rejecting the event...", zap.Error(err))
   142  		h.metrics.EventProcessingFailed(h.eventSourceName, h.eventName)
   143  		return err
   144  	}
   145  
   146  	h.logger.Info("dispatching the event on the data channel...")
   147  	if err = h.dispatch(eventBody); err != nil {
   148  		h.metrics.EventProcessingFailed(h.eventSourceName, h.eventName)
   149  		return err
   150  	}
   151  	return nil
   152  }