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  }