github.com/sereiner/library@v0.0.0-20200518095232-1fa3e640cc5f/mq/stomp/stomp.producer.go (about) 1 package stomp 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/sereiner/library/concurrent/cmap" 12 logger "github.com/sereiner/library/log" 13 "github.com/sereiner/library/mq" 14 "github.com/zkfy/stompngo" 15 ) 16 17 //StompProducer Producer 18 type StompProducer struct { 19 address string 20 conn *stompngo.Connection 21 messages chan *mq.ProcuderMessage 22 backupMsg chan *mq.ProcuderMessage 23 queues cmap.ConcurrentMap 24 connecting bool 25 isConnected bool 26 closeCh chan struct{} 27 done bool 28 lk sync.Mutex 29 header []string 30 *mq.OptionConf 31 } 32 33 //NewStompProducer 创建新的producer 34 func NewStompProducer(address string, opts ...mq.Option) (producer *StompProducer, err error) { 35 producer = &StompProducer{address: address} 36 producer.OptionConf = &mq.OptionConf{} 37 producer.messages = make(chan *mq.ProcuderMessage, 10000) 38 producer.backupMsg = make(chan *mq.ProcuderMessage, 100) 39 producer.closeCh = make(chan struct{}) 40 for _, opt := range opts { 41 opt(producer.OptionConf) 42 } 43 if producer.Logger == nil { 44 producer.Logger = logger.GetSession("mq.producer", logger.CreateSession()) 45 } 46 if strings.EqualFold(producer.OptionConf.Version, "") { 47 producer.OptionConf.Version = "1.1" 48 } 49 if strings.EqualFold(producer.OptionConf.Persistent, "") { 50 producer.OptionConf.Persistent = "true" 51 } 52 if strings.EqualFold(producer.OptionConf.Ack, "") { 53 producer.OptionConf.Ack = "client-individual" 54 } 55 producer.header = stompngo.Headers{"accept-version", producer.OptionConf.Version} 56 return 57 } 58 59 //Connect 循环连接服务器 60 func (producer *StompProducer) Connect() error { 61 err := producer.connectOnce() 62 if err == nil { 63 return nil 64 } 65 producer.Logger.Error(err) 66 go func() { 67 START: 68 for { 69 select { 70 case <-producer.closeCh: 71 break START 72 case <-time.After(time.Second * 3): 73 err = producer.connectOnce() 74 if err == nil { 75 break START 76 } 77 producer.Logger.Error(err) 78 } 79 } 80 }() 81 return nil 82 } 83 84 //sendLoop 循环发送消息 85 func (producer *StompProducer) sendLoop() { 86 if producer.done { 87 producer.disconnect() 88 return 89 } 90 if producer.Retry { 91 Loop1: 92 for { 93 select { 94 case msg, ok := <-producer.backupMsg: 95 if !ok { 96 break Loop1 97 } 98 err := producer.conn.Send(msg.Headers, msg.Data) 99 if err != nil { 100 producer.Logger.Errorf("发送消息失败,放入备份队列(%s)(err:%v)", msg.Queue, err) 101 select { 102 case producer.backupMsg <- msg: 103 default: 104 producer.Logger.Errorf("重试发送失败,备份队列已满无法放入队列(%s):%s", msg.Queue, msg.Data) 105 } 106 break Loop1 107 } 108 case msg, ok := <-producer.messages: 109 if !ok { 110 break Loop1 111 } 112 err := producer.conn.Send(msg.Headers, msg.Data) 113 if err != nil { 114 producer.Logger.Errorf("发送消息失败,放入备份队列(%s)(err:%v)", msg.Queue, err) 115 select { 116 case producer.backupMsg <- msg: 117 default: 118 producer.Logger.Errorf("发送失败,备份队列已满无法放入队列(%s):%s", msg.Queue, msg.Data) 119 } 120 break Loop1 121 } 122 } 123 } 124 } else { 125 Loop2: 126 for { 127 select { 128 case msg, ok := <-producer.messages: 129 if !ok { 130 break Loop2 131 } 132 err := producer.conn.Send(msg.Headers, msg.Data) 133 if err != nil { 134 producer.Logger.Errorf("发送消息失败,放入备份队列(%s)(err:%v)", msg.Queue, err) 135 select { 136 case producer.backupMsg <- msg: 137 default: 138 producer.Logger.Errorf("备份队列已满,无法放入队列(%s):%s", msg.Queue, msg.Data) 139 } 140 break Loop2 141 } 142 } 143 } 144 } 145 if producer.done { //关闭连接 146 producer.disconnect() 147 return 148 } 149 producer.reconnect() 150 } 151 func (producer *StompProducer) disconnect() { 152 if producer.conn == nil || !producer.conn.Connected() { 153 return 154 } 155 producer.conn.Disconnect(stompngo.Headers{}) 156 return 157 } 158 159 //GetBackupMessage 获取备份数据 160 func (producer *StompProducer) GetBackupMessage() chan *mq.ProcuderMessage { 161 return producer.backupMsg 162 } 163 164 //reconnect 自动重连 165 func (producer *StompProducer) reconnect() { 166 producer.conn.Disconnect(stompngo.Headers{}) 167 err := producer.Connect() 168 if err != nil { 169 producer.Logger.Errorf("连接到MQ服务器失败:%v", err) 170 } 171 } 172 173 //ConnectOnce 连接到服务器 174 func (producer *StompProducer) connectOnce() (err error) { 175 if producer.connecting { 176 return nil 177 } 178 producer.lk.Lock() 179 defer producer.lk.Unlock() 180 if producer.connecting { 181 return nil 182 } 183 producer.connecting = true 184 defer func() { 185 producer.connecting = false 186 }() 187 producer.Logger.Infof("重新连接到服务器:%s", producer.address) 188 con, err := net.Dial("tcp", producer.address) 189 if err != nil { 190 return fmt.Errorf("mq 无法连接到远程服务器:%v", err) 191 } 192 producer.conn, err = stompngo.Connect(con, producer.header) 193 if err != nil { 194 return fmt.Errorf("mq 无法连接到MQ:%v", err) 195 } 196 197 go producer.sendLoop() 198 return nil 199 } 200 201 //Send 发送消息 202 func (producer *StompProducer) Send(queue string, msg string, timeout time.Duration) (err error) { 203 if producer.done { 204 return errors.New("mq producer 已关闭") 205 } 206 if !producer.connecting && producer.Retry { 207 return fmt.Errorf("producer无法连接到MQ服务器:%s", producer.address) 208 } 209 210 pm := &mq.ProcuderMessage{Queue: queue, Data: msg, Timeout: timeout} 211 pm.Headers = make([]string, 0, len(producer.header)+4) 212 copy(pm.Headers, producer.header) 213 214 pm.Headers = append(pm.Headers, "destination", "/queue/"+queue) 215 if timeout > 0 && timeout < time.Second*10 { 216 return fmt.Errorf("超时时长不能小于10秒:%s,%v", queue, timeout) 217 } 218 if timeout > 0 { 219 pm.Headers = append(pm.Headers, "expires", 220 fmt.Sprintf("%d000", time.Now().Add(timeout).Unix())) 221 } 222 select { 223 case producer.messages <- pm: 224 return nil 225 default: 226 return errors.New("producer无法连接到MQ服务器,消息队列已满无法发送") 227 } 228 } 229 230 //Close 关闭当前连接 231 func (producer *StompProducer) Close() { 232 producer.done = true 233 close(producer.closeCh) 234 close(producer.messages) 235 close(producer.backupMsg) 236 } 237 238 type stompProducerResolver struct { 239 } 240 241 func (s *stompProducerResolver) Resolve(address string, opts ...mq.Option) (mq.MQProducer, error) { 242 return NewStompProducer(address, opts...) 243 } 244 func init() { 245 mq.RegisterProducer("stomp", &stompProducerResolver{}) 246 }