github.com/lingyao2333/mo-zero@v1.4.1/core/queue/queue.go (about) 1 package queue 2 3 import ( 4 "runtime" 5 "sync" 6 "sync/atomic" 7 "time" 8 9 "github.com/lingyao2333/mo-zero/core/logx" 10 "github.com/lingyao2333/mo-zero/core/rescue" 11 "github.com/lingyao2333/mo-zero/core/stat" 12 "github.com/lingyao2333/mo-zero/core/threading" 13 "github.com/lingyao2333/mo-zero/core/timex" 14 ) 15 16 const queueName = "queue" 17 18 type ( 19 // A Queue is a message queue. 20 Queue struct { 21 name string 22 metrics *stat.Metrics 23 producerFactory ProducerFactory 24 producerRoutineGroup *threading.RoutineGroup 25 consumerFactory ConsumerFactory 26 consumerRoutineGroup *threading.RoutineGroup 27 producerCount int 28 consumerCount int 29 active int32 30 channel chan string 31 quit chan struct{} 32 listeners []Listener 33 eventLock sync.Mutex 34 eventChannels []chan interface{} 35 } 36 37 // A Listener interface represents a listener that can be notified with queue events. 38 Listener interface { 39 OnPause() 40 OnResume() 41 } 42 43 // A Poller interface wraps the method Poll. 44 Poller interface { 45 Name() string 46 Poll() string 47 } 48 49 // A Pusher interface wraps the method Push. 50 Pusher interface { 51 Name() string 52 Push(string) error 53 } 54 ) 55 56 // NewQueue returns a Queue. 57 func NewQueue(producerFactory ProducerFactory, consumerFactory ConsumerFactory) *Queue { 58 q := &Queue{ 59 metrics: stat.NewMetrics(queueName), 60 producerFactory: producerFactory, 61 producerRoutineGroup: threading.NewRoutineGroup(), 62 consumerFactory: consumerFactory, 63 consumerRoutineGroup: threading.NewRoutineGroup(), 64 producerCount: runtime.NumCPU(), 65 consumerCount: runtime.NumCPU() << 1, 66 channel: make(chan string), 67 quit: make(chan struct{}), 68 } 69 q.SetName(queueName) 70 71 return q 72 } 73 74 // AddListener adds a listener to q. 75 func (q *Queue) AddListener(listener Listener) { 76 q.listeners = append(q.listeners, listener) 77 } 78 79 // Broadcast broadcasts message to all event channels. 80 func (q *Queue) Broadcast(message interface{}) { 81 go func() { 82 q.eventLock.Lock() 83 defer q.eventLock.Unlock() 84 85 for _, channel := range q.eventChannels { 86 channel <- message 87 } 88 }() 89 } 90 91 // SetName sets the name of q. 92 func (q *Queue) SetName(name string) { 93 q.name = name 94 q.metrics.SetName(name) 95 } 96 97 // SetNumConsumer sets the number of consumers. 98 func (q *Queue) SetNumConsumer(count int) { 99 q.consumerCount = count 100 } 101 102 // SetNumProducer sets the number of producers. 103 func (q *Queue) SetNumProducer(count int) { 104 q.producerCount = count 105 } 106 107 // Start starts q. 108 func (q *Queue) Start() { 109 q.startProducers(q.producerCount) 110 q.startConsumers(q.consumerCount) 111 112 q.producerRoutineGroup.Wait() 113 close(q.channel) 114 q.consumerRoutineGroup.Wait() 115 } 116 117 // Stop stops q. 118 func (q *Queue) Stop() { 119 close(q.quit) 120 } 121 122 func (q *Queue) consume(eventChan chan interface{}) { 123 var consumer Consumer 124 125 for { 126 var err error 127 if consumer, err = q.consumerFactory(); err != nil { 128 logx.Errorf("Error on creating consumer: %v", err) 129 time.Sleep(time.Second) 130 } else { 131 break 132 } 133 } 134 135 for { 136 select { 137 case message, ok := <-q.channel: 138 if ok { 139 q.consumeOne(consumer, message) 140 } else { 141 logx.Info("Task channel was closed, quitting consumer...") 142 return 143 } 144 case event := <-eventChan: 145 consumer.OnEvent(event) 146 } 147 } 148 } 149 150 func (q *Queue) consumeOne(consumer Consumer, message string) { 151 threading.RunSafe(func() { 152 startTime := timex.Now() 153 defer func() { 154 duration := timex.Since(startTime) 155 q.metrics.Add(stat.Task{ 156 Duration: duration, 157 }) 158 logx.WithDuration(duration).Infof("%s", message) 159 }() 160 161 if err := consumer.Consume(message); err != nil { 162 logx.Errorf("Error occurred while consuming %v: %v", message, err) 163 } 164 }) 165 } 166 167 func (q *Queue) pause() { 168 for _, listener := range q.listeners { 169 listener.OnPause() 170 } 171 } 172 173 func (q *Queue) produce() { 174 var producer Producer 175 176 for { 177 var err error 178 if producer, err = q.producerFactory(); err != nil { 179 logx.Errorf("Error on creating producer: %v", err) 180 time.Sleep(time.Second) 181 } else { 182 break 183 } 184 } 185 186 atomic.AddInt32(&q.active, 1) 187 producer.AddListener(routineListener{ 188 queue: q, 189 }) 190 191 for { 192 select { 193 case <-q.quit: 194 logx.Info("Quitting producer") 195 return 196 default: 197 if v, ok := q.produceOne(producer); ok { 198 q.channel <- v 199 } 200 } 201 } 202 } 203 204 func (q *Queue) produceOne(producer Producer) (string, bool) { 205 // avoid panic quit the producer, just log it and continue 206 defer rescue.Recover() 207 208 return producer.Produce() 209 } 210 211 func (q *Queue) resume() { 212 for _, listener := range q.listeners { 213 listener.OnResume() 214 } 215 } 216 217 func (q *Queue) startConsumers(number int) { 218 for i := 0; i < number; i++ { 219 eventChan := make(chan interface{}) 220 q.eventLock.Lock() 221 q.eventChannels = append(q.eventChannels, eventChan) 222 q.eventLock.Unlock() 223 q.consumerRoutineGroup.Run(func() { 224 q.consume(eventChan) 225 }) 226 } 227 } 228 229 func (q *Queue) startProducers(number int) { 230 for i := 0; i < number; i++ { 231 q.producerRoutineGroup.Run(func() { 232 q.produce() 233 }) 234 } 235 } 236 237 type routineListener struct { 238 queue *Queue 239 } 240 241 func (rl routineListener) OnProducerPause() { 242 if atomic.AddInt32(&rl.queue.active, -1) <= 0 { 243 rl.queue.pause() 244 } 245 } 246 247 func (rl routineListener) OnProducerResume() { 248 if atomic.AddInt32(&rl.queue.active, 1) == 1 { 249 rl.queue.resume() 250 } 251 }