github.com/turingchain2020/turingchain@v1.1.21/queue/client.go (about) 1 // Copyright Turing Corp. 2018 All Rights Reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package queue 6 7 import ( 8 "errors" 9 "sync" 10 "sync/atomic" 11 "time" 12 13 "unsafe" 14 15 "github.com/turingchain2020/turingchain/types" 16 ) 17 18 //消息队列的主要作用是解耦合,让各个模块相对的独立运行。 19 //每个模块都会有一个client 对象 20 //主要的操作大致如下: 21 // client := queue.Client() 22 // client.Sub("topicname") 23 // for msg := range client.Recv() { 24 // process(msg) 25 // } 26 // process 函数会调用 处理具体的消息逻辑 27 28 var gid int64 29 30 // Client 消息队列的接口,每个模块都需要一个发送接受client 31 type Client interface { 32 Send(msg *Message, waitReply bool) (err error) //同步发送消息 33 SendTimeout(msg *Message, waitReply bool, timeout time.Duration) (err error) 34 Wait(msg *Message) (*Message, error) //等待消息处理完成 35 WaitTimeout(msg *Message, timeout time.Duration) (*Message, error) //等待消息处理完成 36 Recv() chan *Message 37 Reply(msg *Message) 38 Sub(topic string) //订阅消息 39 Close() 40 CloseQueue() (*types.Reply, error) 41 NewMessage(topic string, ty int64, data interface{}) (msg *Message) 42 FreeMessage(msg ...*Message) //回收msg, 需要注意回收时上下文不再引用 43 GetConfig() *types.TuringchainConfig 44 } 45 46 // Module be used for module interface 47 type Module interface { 48 SetQueueClient(client Client) 49 //wait for ready 50 Wait() 51 Close() 52 } 53 54 type client struct { 55 q *queue 56 recv chan *Message 57 done chan struct{} 58 wg *sync.WaitGroup 59 topic unsafe.Pointer 60 isClosed int32 61 isCloseing int32 62 } 63 64 func newClient(q *queue) Client { 65 client := &client{} 66 client.q = q 67 client.recv = make(chan *Message, 5) 68 client.done = make(chan struct{}, 1) 69 client.wg = &sync.WaitGroup{} 70 return client 71 } 72 73 // GetConfig return the queue TuringchainConfig 74 func (client *client) GetConfig() *types.TuringchainConfig { 75 types.AssertConfig(client.q) 76 cfg := client.q.GetConfig() 77 if cfg == nil { 78 panic("TuringchainConfig is nil") 79 } 80 return cfg 81 } 82 83 // Send 发送消息,msg 消息 ,waitReply 是否等待回应 84 //1. 系统保证send出去的消息就是成功了,除非系统崩溃 85 //2. 系统保证每个消息都有对应的 response 消息 86 func (client *client) Send(msg *Message, waitReply bool) (err error) { 87 timeout := time.Duration(-1) 88 err = client.SendTimeout(msg, waitReply, timeout) 89 if err == ErrQueueTimeout { 90 panic(err) 91 } 92 return err 93 } 94 95 // SendTimeout 超时发送, msg 消息 ,waitReply 是否等待回应, timeout 超时时间 96 func (client *client) SendTimeout(msg *Message, waitReply bool, timeout time.Duration) (err error) { 97 if client.isClose() { 98 return ErrIsQueueClosed 99 } 100 if !waitReply { 101 //msg.chReply = nil 102 return client.q.sendLowTimeout(msg, timeout) 103 } 104 return client.q.send(msg, timeout) 105 } 106 107 //系统设计出两种优先级别的消息发送 108 //1. SendAsyn 低优先级 109 //2. Send 高优先级别的发送消息 110 111 // NewMessage 新建消息 topic模块名称 ty消息类型 data 数据 112 func (client *client) NewMessage(topic string, ty int64, data interface{}) (msg *Message) { 113 id := atomic.AddInt64(&gid, 1) 114 msg = client.q.msgPool.Get().(*Message) 115 msg.ID = id 116 msg.Ty = ty 117 msg.Data = data 118 msg.Topic = topic 119 return 120 } 121 122 // FreeMessage 回收msg 到内存池 123 func (client *client) FreeMessage(msgs ...*Message) { 124 for _, msg := range msgs { 125 if msg == nil || msg.chReply == nil { 126 continue 127 } 128 msg.Data = nil 129 client.q.msgPool.Put(msg) 130 } 131 } 132 133 func (client *client) Reply(msg *Message) { 134 if msg.chReply != nil { 135 msg.Reply(msg) 136 return 137 } 138 if msg.callback != nil { 139 client.q.callback <- msg 140 } 141 } 142 143 // WaitTimeout 等待时间 msg 消息 timeout 超时时间 144 func (client *client) WaitTimeout(msg *Message, timeout time.Duration) (*Message, error) { 145 if msg.chReply == nil { 146 return &Message{}, errors.New("empty wait channel") 147 } 148 149 var t <-chan time.Time 150 if timeout > 0 { 151 timer := time.NewTimer(timeout) 152 defer timer.Stop() 153 t = timer.C 154 } 155 select { 156 case msg = <-msg.chReply: 157 return msg, msg.Err() 158 case <-client.done: 159 return &Message{}, ErrIsQueueClosed 160 case <-t: 161 return &Message{}, ErrQueueTimeout 162 } 163 } 164 165 // Wait 等待时间 166 func (client *client) Wait(msg *Message) (*Message, error) { 167 timeout := time.Duration(-1) 168 msg, err := client.WaitTimeout(msg, timeout) 169 if err == ErrQueueTimeout { 170 panic(err) 171 } 172 return msg, err 173 } 174 175 // Recv 获取接受消息通道 176 func (client *client) Recv() chan *Message { 177 return client.recv 178 } 179 180 func (client *client) getTopic() string { 181 return *(*string)(atomic.LoadPointer(&client.topic)) 182 } 183 184 func (client *client) setTopic(topic string) { 185 // #nosec 186 atomic.StorePointer(&client.topic, unsafe.Pointer(&topic)) 187 } 188 189 func (client *client) isClose() bool { 190 return atomic.LoadInt32(&client.isClosed) == 1 191 } 192 193 func (client *client) isInClose() bool { 194 return atomic.LoadInt32(&client.isCloseing) == 1 195 } 196 197 // Close 关闭client 198 func (client *client) Close() { 199 if atomic.LoadInt32(&client.isClosed) == 1 || atomic.LoadPointer(&client.topic) == nil { 200 return 201 } 202 topic := client.getTopic() 203 client.q.closeTopic(topic) 204 close(client.done) 205 atomic.StoreInt32(&client.isCloseing, 1) 206 client.wg.Wait() 207 atomic.StoreInt32(&client.isClosed, 1) 208 close(client.Recv()) 209 for msg := range client.Recv() { 210 msg.Reply(client.NewMessage(msg.Topic, msg.Ty, types.ErrChannelClosed)) 211 } 212 } 213 214 // CloseQueue 关闭消息队列 215 func (client *client) CloseQueue() (*types.Reply, error) { 216 // client.q.Close() 217 if client.q.isClosed() { 218 return &types.Reply{IsOk: true}, nil 219 } 220 qlog.Debug("queue", "msg", "closing turingchain") 221 client.q.interrupt <- struct{}{} 222 // close(client.q.interupt) 223 return &types.Reply{IsOk: true}, nil 224 } 225 226 func (client *client) isEnd(data *Message, ok bool) bool { 227 if !ok { 228 return true 229 } 230 if data.Data == nil && data.ID == 0 && data.Ty == 0 { 231 return true 232 } 233 if atomic.LoadInt32(&client.isClosed) == 1 { 234 return true 235 } 236 return false 237 } 238 239 // Sub 订阅消息类型 240 func (client *client) Sub(topic string) { 241 //正在关闭或者已经关闭 242 if client.isInClose() || client.isClose() { 243 return 244 } 245 client.wg.Add(1) 246 client.setTopic(topic) 247 sub := client.q.chanSub(topic) 248 go func() { 249 defer func() { 250 client.wg.Done() 251 }() 252 for { 253 select { 254 case data, ok := <-sub.high: 255 if client.isEnd(data, ok) { 256 qlog.Info("unsub1", "topic", topic) 257 return 258 } 259 client.Recv() <- data 260 default: 261 select { 262 case data, ok := <-sub.high: 263 if client.isEnd(data, ok) { 264 qlog.Info("unsub2", "topic", topic) 265 return 266 } 267 client.Recv() <- data 268 case data, ok := <-sub.low: 269 if client.isEnd(data, ok) { 270 qlog.Info("unsub3", "topic", topic) 271 return 272 } 273 client.Recv() <- data 274 case <-client.done: 275 qlog.Error("unsub4", "topic", topic) 276 return 277 } 278 } 279 } 280 }() 281 }