github.com/turingchain2020/turingchain@v1.1.21/queue/queue.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 turingchain底层消息队列模块 6 package queue 7 8 import ( 9 "errors" 10 "fmt" 11 "os" 12 "os/signal" 13 "sync" 14 "sync/atomic" 15 "syscall" 16 "time" 17 18 "github.com/turingchain2020/turingchain/types" 19 20 log "github.com/turingchain2020/turingchain/common/log/log15" 21 ) 22 23 //消息队列: 24 //多对多消息队列 25 //消息:topic 26 27 //1. 队列特点: 28 //1.1 一个topic 只有一个订阅者(以后会变成多个)目前基本够用,模块都只有一个实例. 29 //1.2 消息的回复直接通过消息自带的channel 回复 30 var qlog = log.New("module", "queue") 31 32 const ( 33 defaultChanBuffer = 64 34 defaultLowChanBuffer = 40960 35 ) 36 37 //消息队列的错误 38 var ( 39 ErrIsQueueClosed = errors.New("ErrIsQueueClosed") 40 ErrQueueTimeout = errors.New("ErrQueueTimeout") 41 ErrQueueChannelFull = errors.New("ErrQueueChannelFull") 42 ) 43 44 // DisableLog disable log 45 func DisableLog() { 46 qlog.SetHandler(log.DiscardHandler()) 47 } 48 49 type chanSub struct { 50 high chan *Message 51 low chan *Message 52 isClose int32 53 } 54 55 // Queue only one obj in project 56 // Queue only generate Client and start、Close operate, 57 // if you send massage or receive massage on Queue, please use Client. 58 type Queue interface { 59 Close() 60 Start() 61 Client() Client 62 Name() string 63 SetConfig(cfg *types.TuringchainConfig) 64 GetConfig() *types.TuringchainConfig 65 } 66 67 type queue struct { 68 chanSubs map[string]*chanSub 69 mu sync.Mutex 70 done chan struct{} 71 interrupt chan struct{} 72 callback chan *Message 73 isClose int32 74 name string 75 cfg *types.TuringchainConfig 76 msgPool *sync.Pool 77 } 78 79 // New new queue struct 80 func New(name string) Queue { 81 q := &queue{ 82 chanSubs: make(map[string]*chanSub), 83 name: name, 84 done: make(chan struct{}, 1), 85 interrupt: make(chan struct{}, 1), 86 callback: make(chan *Message, 1024), 87 } 88 q.msgPool = &sync.Pool{ 89 New: func() interface{} { 90 return &Message{ 91 chReply: make(chan *Message, 1), 92 } 93 }, 94 } 95 go func() { 96 for { 97 select { 98 case <-q.done: 99 qlog.Info("closing turingchain callback") 100 return 101 case msg := <-q.callback: 102 if msg.callback != nil { 103 msg.callback(msg) 104 } 105 } 106 } 107 }() 108 return q 109 } 110 111 // GetConfig return the queue TuringchainConfig 112 func (q *queue) GetConfig() *types.TuringchainConfig { 113 return q.cfg 114 } 115 116 // Name return the queue name 117 func (q *queue) SetConfig(cfg *types.TuringchainConfig) { 118 if cfg == nil { 119 panic("set config is nil") 120 } 121 if q.cfg != nil { 122 panic("do not reset queue config") 123 } 124 q.cfg = cfg 125 } 126 127 // Name return the queue name 128 func (q *queue) Name() string { 129 return q.name 130 } 131 132 // Start 开始运行消息队列 133 func (q *queue) Start() { 134 c := make(chan os.Signal, 1) 135 signal.Notify(c, os.Interrupt, syscall.SIGTERM) 136 // Block until a signal is received. 137 select { 138 case <-q.done: 139 qlog.Info("closing turingchain done") 140 //atomic.StoreInt32(&q.isClose, 1) 141 break 142 case <-q.interrupt: 143 qlog.Info("closing turingchain") 144 //atomic.StoreInt32(&q.isClose, 1) 145 break 146 case s := <-c: 147 qlog.Info("Got signal:", s) 148 //atomic.StoreInt32(&q.isClose, 1) 149 break 150 } 151 } 152 153 func (q *queue) isClosed() bool { 154 return atomic.LoadInt32(&q.isClose) == 1 155 } 156 157 // Close 关闭消息队列 158 func (q *queue) Close() { 159 if q.isClosed() { 160 return 161 } 162 q.mu.Lock() 163 for topic, ch := range q.chanSubs { 164 if ch.isClose == 0 { 165 ch.high <- &Message{} 166 ch.low <- &Message{} 167 q.chanSubs[topic] = &chanSub{isClose: 1} 168 } 169 } 170 q.mu.Unlock() 171 q.done <- struct{}{} 172 close(q.done) 173 atomic.StoreInt32(&q.isClose, 1) 174 qlog.Info("queue module closed") 175 } 176 177 func (q *queue) chanSub(topic string) *chanSub { 178 q.mu.Lock() 179 defer q.mu.Unlock() 180 _, ok := q.chanSubs[topic] 181 if !ok { 182 q.chanSubs[topic] = &chanSub{ 183 high: make(chan *Message, defaultChanBuffer), 184 low: make(chan *Message, defaultLowChanBuffer), 185 isClose: 0, 186 } 187 } 188 return q.chanSubs[topic] 189 } 190 191 func (q *queue) closeTopic(topic string) { 192 q.mu.Lock() 193 defer q.mu.Unlock() 194 sub, ok := q.chanSubs[topic] 195 if !ok { 196 return 197 } 198 if sub.isClose == 0 { 199 sub.high <- &Message{} 200 sub.low <- &Message{} 201 } 202 q.chanSubs[topic] = &chanSub{isClose: 1} 203 } 204 205 func (q *queue) send(msg *Message, timeout time.Duration) (err error) { 206 if q.isClosed() { 207 return types.ErrChannelClosed 208 } 209 sub := q.chanSub(msg.Topic) 210 if sub.isClose == 1 { 211 return types.ErrChannelClosed 212 } 213 if timeout == -1 { 214 sub.high <- msg 215 return nil 216 } 217 defer func() { 218 res := recover() 219 if res != nil { 220 err = res.(error) 221 } 222 }() 223 if timeout == 0 { 224 select { 225 case sub.high <- msg: 226 return nil 227 default: 228 qlog.Error("send chainfull", "msg", msg, "topic", msg.Topic, "sub", sub) 229 return ErrQueueChannelFull 230 } 231 } 232 t := time.NewTimer(timeout) 233 defer t.Stop() 234 select { 235 case sub.high <- msg: 236 case <-t.C: 237 qlog.Error("send timeout", "msg", msg, "topic", msg.Topic, "sub", sub) 238 return ErrQueueTimeout 239 } 240 return nil 241 } 242 243 func (q *queue) sendAsyn(msg *Message) error { 244 if q.isClosed() { 245 return types.ErrChannelClosed 246 } 247 sub := q.chanSub(msg.Topic) 248 if sub.isClose == 1 { 249 return types.ErrChannelClosed 250 } 251 select { 252 case sub.low <- msg: 253 return nil 254 default: 255 qlog.Error("send asyn err", "msg", msg, "err", ErrQueueChannelFull) 256 return ErrQueueChannelFull 257 } 258 } 259 260 func (q *queue) sendLowTimeout(msg *Message, timeout time.Duration) error { 261 if q.isClosed() { 262 return types.ErrChannelClosed 263 } 264 sub := q.chanSub(msg.Topic) 265 if sub.isClose == 1 { 266 return types.ErrChannelClosed 267 } 268 if timeout == -1 { 269 sub.low <- msg 270 return nil 271 } 272 if timeout == 0 { 273 return q.sendAsyn(msg) 274 } 275 t := time.NewTimer(timeout) 276 defer t.Stop() 277 select { 278 case sub.low <- msg: 279 return nil 280 case <-t.C: 281 qlog.Error("send asyn timeout", "msg", msg) 282 return ErrQueueTimeout 283 } 284 } 285 286 // Client new client 287 func (q *queue) Client() Client { 288 return newClient(q) 289 } 290 291 // Message message struct 292 type Message struct { 293 Topic string 294 Ty int64 295 ID int64 296 Data interface{} 297 chReply chan *Message 298 callback func(msg *Message) 299 } 300 301 // NewMessage new message 302 func NewMessage(id int64, topic string, ty int64, data interface{}) (msg *Message) { 303 msg = &Message{} 304 msg.ID = id 305 msg.Ty = ty 306 msg.Data = data 307 msg.Topic = topic 308 msg.chReply = make(chan *Message, 1) 309 return msg 310 } 311 312 // NewMessageCallback reply block 313 func NewMessageCallback(id int64, topic string, ty int64, data interface{}, callback func(msg *Message)) (msg *Message) { 314 msg = &Message{} 315 msg.ID = id 316 msg.Ty = ty 317 msg.Data = data 318 msg.Topic = topic 319 msg.callback = callback 320 return msg 321 } 322 323 // GetData get message data 324 func (msg *Message) GetData() interface{} { 325 if _, ok := msg.Data.(error); ok { 326 return nil 327 } 328 return msg.Data 329 } 330 331 // Err if err return error msg, or return nil 332 func (msg *Message) Err() error { 333 if err, ok := msg.Data.(error); ok { 334 return err 335 } 336 return nil 337 } 338 339 // Reply reply message to reply chan 340 func (msg *Message) Reply(replyMsg *Message) { 341 if msg.chReply == nil { 342 qlog.Debug("reply a empty chreply", "msg", msg) 343 return 344 } 345 msg.chReply <- replyMsg 346 } 347 348 // String print the message information 349 func (msg *Message) String() string { 350 return fmt.Sprintf("{topic:%s, Ty:%s, Id:%d, Err:%v, Ch:%v}", msg.Topic, 351 types.GetEventName(int(msg.Ty)), msg.ID, msg.Err(), msg.chReply != nil) 352 } 353 354 // ReplyErr reply error 355 func (msg *Message) ReplyErr(title string, err error) { 356 var reply types.Reply 357 if err != nil { 358 qlog.Error(title, "reply.err", err.Error()) 359 reply.IsOk = false 360 reply.Msg = []byte(err.Error()) 361 } else { 362 qlog.Debug(title, "success", "ok") 363 reply.IsOk = true 364 } 365 id := atomic.AddInt64(&gid, 1) 366 msg.Reply(NewMessage(id, "", types.EventReply, &reply)) 367 }