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  }