github.com/wfusion/gofusion@v1.1.14/common/infra/watermill/pubsub/pulsar/publisher.go (about) 1 package pulsar 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/apache/pulsar-client-go/pulsar" 9 "github.com/pkg/errors" 10 "go.uber.org/multierr" 11 12 "github.com/wfusion/gofusion/common/infra/watermill" 13 "github.com/wfusion/gofusion/common/infra/watermill/message" 14 ) 15 16 // PublisherConfig is the configuration to create a publisher 17 type PublisherConfig struct { 18 // URL is the Pulsar URL. 19 URL string 20 21 Authentication pulsar.Authentication 22 23 AppID string 24 } 25 26 // Publisher provides the pulsar implementation for watermill publish operations 27 type Publisher struct { 28 conn pulsar.Client 29 pubs map[string]pulsar.Producer 30 31 logger watermill.LoggerAdapter 32 config PublisherConfig 33 closed bool 34 } 35 36 // NewPublisher creates a new Publisher. 37 func NewPublisher(config PublisherConfig, logger watermill.LoggerAdapter) (*Publisher, error) { 38 conn, err := pulsar.NewClient(pulsar.ClientOptions{ 39 URL: config.URL, 40 Authentication: config.Authentication, 41 }) 42 if err != nil { 43 return nil, errors.Wrap(err, "cannot connect to nats") 44 } 45 46 return NewPublisherWithPulsarClient(config, logger, conn) 47 } 48 49 // NewPublisherWithPulsarClient creates a new Publisher with the provided nats connection. 50 func NewPublisherWithPulsarClient(config PublisherConfig, logger watermill.LoggerAdapter, 51 conn pulsar.Client) (*Publisher, error) { 52 if logger == nil { 53 logger = watermill.NopLogger{} 54 } 55 56 return &Publisher{ 57 conn: conn, 58 logger: logger, 59 config: config, 60 pubs: make(map[string]pulsar.Producer), 61 }, nil 62 } 63 64 // Publish publishes message to Pulsar. 65 // 66 // Publish will not return until an ack has been received from JetStream. 67 // When one of messages delivery fails - function is interrupted. 68 func (p *Publisher) Publish(ctx context.Context, topic string, messages ...*message.Message) (err error) { 69 if p.closed { 70 return errors.New("publisher closed") 71 } 72 73 ctx, cancel := context.WithCancel(context.Background()) 74 defer cancel() 75 76 producer, found := p.pubs[topic] 77 78 if !found { 79 pr, err := p.conn.CreateProducer(pulsar.ProducerOptions{Topic: topic}) 80 81 if err != nil { 82 return err 83 } 84 85 producer = pr 86 p.pubs[topic] = producer 87 } 88 89 for _, msg := range messages { 90 messageFields := watermill.LogFields{ 91 "message_uuid": msg.UUID, 92 "topic_name": topic, 93 } 94 msg.Metadata[watermill.MessageHeaderAppID] = p.config.AppID 95 96 p.logger.Trace("Publishing message", messageFields) 97 98 msgID, sendErr := producer.Send(ctx, &pulsar.ProducerMessage{ 99 Key: msg.UUID, 100 Payload: msg.Payload, 101 Properties: msg.Metadata, 102 EventTime: time.Now(), 103 }) 104 if sendErr != nil { 105 err = multierr.Append(err, sendErr) 106 p.logger.Trace(fmt.Sprintf("Publishing message failed: %s", err), messageFields) 107 continue 108 } 109 messageFields = messageFields.Add(watermill.LogFields{"message_raw_id": msgID.String()}) 110 111 p.logger.Trace("Publishing message success", messageFields) 112 } 113 114 return nil 115 } 116 117 // Close closes the publisher and the underlying connection 118 func (p *Publisher) Close() error { 119 if p.closed { 120 return nil 121 } 122 p.closed = true 123 124 p.logger.Trace("Closing publisher", nil) 125 defer p.logger.Trace("Publisher closed", nil) 126 127 for _, pub := range p.pubs { 128 pub.Close() 129 } 130 131 p.conn.Close() 132 133 return nil 134 }