github.com/wfusion/gofusion@v1.1.14/common/infra/watermill/pubsub/redis/publisher.go (about) 1 package redis 2 3 import ( 4 "context" 5 "sync" 6 7 "github.com/pkg/errors" 8 "github.com/redis/go-redis/v9" 9 10 "github.com/wfusion/gofusion/common/infra/watermill" 11 "github.com/wfusion/gofusion/common/infra/watermill/message" 12 ) 13 14 type Publisher struct { 15 config PublisherConfig 16 client redis.UniversalClient 17 logger watermill.LoggerAdapter 18 19 closed bool 20 closeMutex sync.Mutex 21 } 22 23 // NewPublisher creates a new redis stream Publisher. 24 func NewPublisher(config PublisherConfig, logger watermill.LoggerAdapter) (*Publisher, error) { 25 config.setDefaults() 26 27 if err := config.Validate(); err != nil { 28 return nil, err 29 } 30 31 if logger == nil { 32 logger = &watermill.NopLogger{} 33 } 34 35 return &Publisher{ 36 config: config, 37 client: config.Client, 38 logger: logger, 39 closed: false, 40 }, nil 41 } 42 43 type PublisherConfig struct { 44 Client redis.UniversalClient 45 Marshaller Marshaller 46 Maxlens map[string]int64 47 DisableRedisConnClose bool 48 } 49 50 func (c *PublisherConfig) setDefaults() { 51 if c.Marshaller == nil { 52 c.Marshaller = DefaultMarshallerUnmarshaller{} 53 } 54 } 55 56 func (c *PublisherConfig) Validate() error { 57 if c.Client == nil { 58 return errors.New("redis client is empty") 59 } 60 for topic, maxlen := range c.Maxlens { 61 if maxlen < 0 { 62 // zero maxlen stream indicates unlimited stream length 63 c.Maxlens[topic] = 0 64 } 65 } 66 return nil 67 } 68 69 // Publish publishes message to redis stream 70 // 71 // Publish is blocking and waits for redis response. 72 // When any of messages delivery fails - function is interrupted. 73 func (p *Publisher) Publish(ctx context.Context, topic string, msgs ...*message.Message) error { 74 if p.closed { 75 return errors.New("publisher closed") 76 } 77 78 logFields := make(watermill.LogFields, 4) 79 logFields["topic"] = topic 80 81 for _, msg := range msgs { 82 logFields["message_uuid"] = msg.UUID 83 p.logger.Trace("[Common] watermill redis sending message to redis stream", logFields) 84 85 values, err := p.config.Marshaller.Marshal(topic, msg) 86 if err != nil { 87 return errors.Wrapf(err, "cannot marshal message %s", msg.UUID) 88 } 89 90 maxlen, ok := p.config.Maxlens[topic] 91 if !ok { 92 maxlen = 0 93 } 94 95 id, err := p.client.XAdd(ctx, &redis.XAddArgs{ 96 Stream: topic, 97 Values: values, 98 MaxLen: maxlen, 99 Approx: true, 100 }).Result() 101 if err != nil { 102 return errors.Wrapf(err, "cannot xadd message %s", msg.UUID) 103 } 104 105 logFields["xadd_id"] = id 106 logFields["message_raw_id"] = id 107 p.logger.Trace("[Common] watermill redis message sent to redis stream", logFields) 108 } 109 110 return nil 111 } 112 113 func (p *Publisher) Close() error { 114 p.closeMutex.Lock() 115 defer p.closeMutex.Unlock() 116 117 if p.closed { 118 return nil 119 } 120 p.closed = true 121 122 if !p.config.DisableRedisConnClose { 123 if err := p.client.Close(); err != nil { 124 return err 125 } 126 } 127 128 return nil 129 }