github.com/sereiner/library@v0.0.0-20200518095232-1fa3e640cc5f/mq/stomp/stomp.consumer.go (about) 1 package stomp 2 3 import ( 4 "fmt" 5 "net" 6 "strings" 7 "sync" 8 "time" 9 10 "errors" 11 12 "github.com/sereiner/library/concurrent/cmap" 13 logger "github.com/sereiner/library/log" 14 "github.com/sereiner/library/mq" 15 "github.com/zkfy/stompngo" 16 ) 17 18 type consumerChan struct { 19 msgChan <-chan stompngo.MessageData 20 unconsumeCh chan struct{} 21 } 22 23 //StompConsumer Consumer 24 type StompConsumer struct { 25 address string 26 conn *stompngo.Connection 27 cache cmap.ConcurrentMap 28 queues cmap.ConcurrentMap 29 connecting bool 30 closeCh chan struct{} 31 done bool 32 lk sync.Mutex 33 header []string 34 once sync.Once 35 *mq.OptionConf 36 } 37 38 //NewStompConsumer 创建新的Consumer 39 func NewStompConsumer(address string, opts ...mq.Option) (consumer *StompConsumer, err error) { 40 consumer = &StompConsumer{address: address} 41 consumer.OptionConf = &mq.OptionConf{Logger: logger.GetSession("mq.consumer", logger.CreateSession())} 42 consumer.closeCh = make(chan struct{}) 43 consumer.queues = cmap.New(2) 44 consumer.cache = cmap.New(2) 45 for _, opt := range opts { 46 opt(consumer.OptionConf) 47 } 48 if strings.EqualFold(consumer.OptionConf.Version, "") { 49 consumer.OptionConf.Version = "1.1" 50 } 51 if strings.EqualFold(consumer.OptionConf.Persistent, "") { 52 consumer.OptionConf.Persistent = "true" 53 } 54 if strings.EqualFold(consumer.OptionConf.Ack, "") { 55 consumer.OptionConf.Ack = "client-individual" 56 } 57 consumer.header = stompngo.Headers{"accept-version", consumer.OptionConf.Version} 58 return 59 } 60 61 //Connect 循环连接服务器 62 func (consumer *StompConsumer) Connect() error { 63 err := consumer.ConnectOnce() 64 if err == nil { 65 return nil 66 } 67 consumer.Logger.Error(err) 68 go func() { 69 START: 70 for { 71 select { 72 case <-consumer.closeCh: 73 break START 74 case <-time.After(time.Second * 3): 75 err = consumer.ConnectOnce() 76 if err == nil { 77 break START 78 } 79 consumer.Logger.Error(err) 80 } 81 } 82 }() 83 return nil 84 } 85 86 //ConnectOnce 连接到服务器 87 func (consumer *StompConsumer) ConnectOnce() (err error) { 88 if consumer.connecting { 89 return nil 90 } 91 consumer.lk.Lock() 92 defer consumer.lk.Unlock() 93 if consumer.connecting { 94 return nil 95 } 96 consumer.connecting = true 97 defer func() { 98 consumer.connecting = false 99 }() 100 con, err := net.Dial("tcp", consumer.address) 101 if err != nil { 102 return fmt.Errorf("mq 无法连接到远程服务器:%v", err) 103 } 104 consumer.conn, err = stompngo.Connect(con, consumer.header) 105 if err != nil { 106 return fmt.Errorf("mq 无法连接到MQ:%v", err) 107 } 108 109 //连接成功后开始订阅消息 110 consumer.cache.IterCb(func(key string, value interface{}) bool { 111 go func() { 112 err = consumer.consume(key, value.(func(mq.IMessage))) 113 if err != nil { 114 consumer.Logger.Errorf("consume失败:%v", err) 115 } 116 }() 117 return true 118 }) 119 120 return nil 121 } 122 123 //Consume 订阅消息 124 func (consumer *StompConsumer) Consume(queue string, concurrency int, callback func(mq.IMessage)) (err error) { 125 if strings.EqualFold(queue, "") { 126 return errors.New("队列名字不能为空") 127 } 128 if callback == nil { 129 return errors.New("回调函数不能为nil") 130 } 131 b, _ := consumer.cache.SetIfAbsent(queue, callback) 132 if !b { 133 err = fmt.Errorf("重复订阅消息:%s", queue) 134 return 135 } 136 return nil 137 } 138 139 //Consume 注册消费信息 140 func (consumer *StompConsumer) consume(queue string, callback func(mq.IMessage)) (err error) { 141 success, ch, err := consumer.queues.SetIfAbsentCb(queue, func(input ...interface{}) (c interface{}, err error) { 142 queue := input[0].(string) 143 header := stompngo.Headers{"destination", fmt.Sprintf("/%s/%s", "queue", queue), "ack", consumer.Ack} 144 consumer.conn.SetSubChanCap(10) 145 msgChan, err := consumer.conn.Subscribe(header) 146 if err != nil { 147 return 148 } 149 chans := &consumerChan{} 150 chans.msgChan = msgChan 151 chans.unconsumeCh = make(chan struct{}) 152 return chans, nil 153 }, queue) 154 if err != nil { 155 return err 156 } 157 if !success { 158 err = fmt.Errorf("重复订阅消息:%s", queue) 159 return 160 } 161 msgChan := ch.(*consumerChan) 162 START: 163 for { 164 select { 165 case <-consumer.closeCh: 166 break START 167 case <-msgChan.unconsumeCh: 168 break START 169 case msg, ok := <-msgChan.msgChan: 170 if !ok { 171 break START 172 } 173 message := NewStompMessage(consumer, &msg.Message) 174 if message.Has() { 175 go callback(message) 176 } else { 177 consumer.reconnect(queue) 178 break START 179 } 180 } 181 } 182 return 183 } 184 func (consumer *StompConsumer) reconnect(queue string) { 185 if v, b := consumer.queues.Get(queue); b { 186 ch := v.(*consumerChan) 187 close(ch.unconsumeCh) 188 } 189 consumer.queues.Remove(queue) 190 consumer.conn.Disconnect(stompngo.Headers{}) 191 consumer.Connect() 192 } 193 194 //UnConsume 取消注册消费 195 func (consumer *StompConsumer) UnConsume(queue string) { 196 if consumer.conn == nil { 197 return 198 } 199 header := stompngo.Headers{"destination", 200 fmt.Sprintf("/%s/%s", "queue", queue), "ack", consumer.Ack} 201 consumer.conn.Unsubscribe(header) 202 if v, b := consumer.queues.Get(queue); b { 203 ch := v.(*consumerChan) 204 close(ch.unconsumeCh) 205 } 206 consumer.queues.Remove(queue) 207 consumer.cache.Remove(queue) 208 } 209 210 //Close 关闭当前连接 211 func (consumer *StompConsumer) Close() { 212 213 if consumer.conn == nil { 214 return 215 } 216 consumer.once.Do(func() { 217 close(consumer.closeCh) 218 }) 219 220 consumer.queues.RemoveIterCb(func(key string, value interface{}) bool { 221 ch := value.(*consumerChan) 222 close(ch.unconsumeCh) 223 return true 224 }) 225 consumer.cache.Clear() 226 go func() { 227 defer recover() 228 time.Sleep(time.Millisecond * 100) 229 consumer.conn.Disconnect(stompngo.Headers{}) 230 }() 231 232 } 233 234 type stompConsumerResolver struct { 235 } 236 237 func (s *stompConsumerResolver) Resolve(address string, opts ...mq.Option) (mq.MQConsumer, error) { 238 return NewStompConsumer(address, opts...) 239 } 240 func init() { 241 mq.RegisterCosnumer("stomp", &stompConsumerResolver{}) 242 }