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  }