github.com/wfusion/gofusion@v1.1.14/common/infra/watermill/message/message.go (about) 1 package message 2 3 import ( 4 "bytes" 5 "context" 6 "sync" 7 ) 8 9 var closedchan = make(chan struct{}) 10 11 func init() { 12 close(closedchan) 13 } 14 15 // Payload is the Message's payload. 16 type Payload []byte 17 18 // Message is the basic transfer unit. 19 // Messages are emitted by Publishers and received by Subscribers. 20 type Message struct { 21 // UUID is a unique identifier of message. 22 // 23 // It is only used by Watermill for debugging. 24 // UUID can be empty. 25 UUID string 26 27 // Metadata contains the message metadata. 28 // 29 // Can be used to store data which doesn't require unmarshalling the entire payload. 30 // It is something similar to HTTP request's headers. 31 // 32 // Metadata is marshaled and will be saved to the PubSub. 33 Metadata Metadata 34 35 // Payload is the message's payload. 36 Payload Payload 37 38 // ack is closed, when acknowledge is received. 39 ack chan struct{} 40 // noACk is closed, when negative acknowledge is received. 41 noAck chan struct{} 42 43 ackMutex sync.Mutex 44 ackSentType ackType 45 46 ctx context.Context 47 } 48 49 // NewMessage creates a new Message with given uuid and payload. 50 func NewMessage(uuid string, payload Payload) *Message { 51 return &Message{ 52 UUID: uuid, 53 Metadata: make(map[string]string), 54 Payload: payload, 55 ack: make(chan struct{}), 56 noAck: make(chan struct{}), 57 } 58 } 59 60 type ackType int 61 62 const ( 63 noAckSent ackType = iota 64 ack 65 nack 66 ) 67 68 // Equals compare, that two messages are equal. Acks/Nacks are not compared. 69 func (m *Message) Equals(toCompare *Message) bool { 70 if m.UUID != toCompare.UUID { 71 return false 72 } 73 if len(m.Metadata) != len(toCompare.Metadata) { 74 return false 75 } 76 for key, value := range m.Metadata { 77 if value != toCompare.Metadata[key] { 78 return false 79 } 80 } 81 return bytes.Equal(m.Payload, toCompare.Payload) 82 } 83 84 // Ack sends message's acknowledgement. 85 // 86 // Ack is not blocking. 87 // Ack is idempotent. 88 // False is returned, if Nack is already sent. 89 func (m *Message) Ack() bool { 90 m.ackMutex.Lock() 91 defer m.ackMutex.Unlock() 92 93 if m.ackSentType == nack { 94 return false 95 } 96 if m.ackSentType != noAckSent { 97 return true 98 } 99 100 m.ackSentType = ack 101 if m.ack == nil { 102 m.ack = closedchan 103 } else { 104 close(m.ack) 105 } 106 107 return true 108 } 109 110 // Nack sends message's negative acknowledgement. 111 // 112 // Nack is not blocking. 113 // Nack is idempotent. 114 // False is returned, if Ack is already sent. 115 func (m *Message) Nack() bool { 116 m.ackMutex.Lock() 117 defer m.ackMutex.Unlock() 118 119 if m.ackSentType == ack { 120 return false 121 } 122 if m.ackSentType != noAckSent { 123 return true 124 } 125 126 m.ackSentType = nack 127 128 if m.noAck == nil { 129 m.noAck = closedchan 130 } else { 131 close(m.noAck) 132 } 133 134 return true 135 } 136 137 // Acked returns channel which is closed when acknowledgement is sent. 138 // 139 // Usage: 140 // 141 // select { 142 // case <-message.Acked(): 143 // // ack received 144 // case <-message.Nacked(): 145 // // nack received 146 // } 147 func (m *Message) Acked() <-chan struct{} { 148 return m.ack 149 } 150 151 // Nacked returns channel which is closed when negative acknowledgement is sent. 152 // 153 // Usage: 154 // 155 // select { 156 // case <-message.Acked(): 157 // // ack received 158 // case <-message.Nacked(): 159 // // nack received 160 // } 161 func (m *Message) Nacked() <-chan struct{} { 162 return m.noAck 163 } 164 165 // Context returns the message's context. To change the context, use 166 // SetContext. 167 // 168 // The returned context is always non-nil; it defaults to the 169 // background context. 170 func (m *Message) Context() context.Context { 171 if m.ctx != nil { 172 return m.ctx 173 } 174 return context.Background() 175 } 176 177 // SetContext sets provided context to the message. 178 func (m *Message) SetContext(ctx context.Context) { 179 m.ctx = ctx 180 } 181 182 // Copy copies all message without Acks/Nacks. 183 // The context is not propagated to the copy. 184 func (m *Message) Copy() *Message { 185 msg := NewMessage(m.UUID, m.Payload) 186 for k, v := range m.Metadata { 187 msg.Metadata.Set(k, v) 188 } 189 return msg 190 }