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 }