github.com/godaddy-x/freego@v1.0.156/amqp/publish.go (about)

     1  package rabbitmq
     2  
     3  import (
     4  	"fmt"
     5  	DIC "github.com/godaddy-x/freego/common"
     6  	"github.com/godaddy-x/freego/utils"
     7  	"github.com/godaddy-x/freego/zlog"
     8  	"github.com/streadway/amqp"
     9  	"sync"
    10  	"time"
    11  )
    12  
    13  var (
    14  	publishMgrs = make(map[string]*PublishManager)
    15  )
    16  
    17  type PublishManager struct {
    18  	mu0      sync.Mutex
    19  	mu       sync.Mutex
    20  	conf     AmqpConfig
    21  	conn     *amqp.Connection
    22  	channels map[string]*PublishMQ
    23  }
    24  
    25  type PublishMQ struct {
    26  	mu      sync.Mutex
    27  	ready   bool
    28  	option  *Option
    29  	channel *amqp.Channel
    30  	queue   *amqp.Queue
    31  }
    32  
    33  type QueueData struct {
    34  	Name      string
    35  	Consumers int
    36  	Messages  int
    37  }
    38  
    39  func (self *PublishManager) InitConfig(input ...AmqpConfig) (*PublishManager, error) {
    40  	for _, v := range input {
    41  		if _, b := publishMgrs[v.DsName]; b {
    42  			return nil, utils.Error("rabbitmq publish init failed: [", v.DsName, "] exist")
    43  		}
    44  		if len(v.DsName) == 0 {
    45  			v.DsName = DIC.MASTER
    46  		}
    47  		publishMgr := &PublishManager{
    48  			conf:     v,
    49  			channels: make(map[string]*PublishMQ),
    50  		}
    51  		if _, err := publishMgr.Connect(); err != nil {
    52  			return nil, err
    53  		}
    54  		publishMgrs[v.DsName] = publishMgr
    55  		zlog.Printf("rabbitmq publish service【%s】has been started successful", v.DsName)
    56  	}
    57  	return self, nil
    58  }
    59  
    60  func (self *PublishManager) Client(ds ...string) (*PublishManager, error) {
    61  	dsName := DIC.MASTER
    62  	if len(ds) > 0 && len(ds[0]) > 0 {
    63  		dsName = ds[0]
    64  	}
    65  	return publishMgrs[dsName], nil
    66  }
    67  
    68  func NewPublish(ds ...string) (*PublishManager, error) {
    69  	return new(PublishManager).Client(ds...)
    70  }
    71  
    72  func (self *PublishManager) Connect() (*PublishManager, error) {
    73  	conn, err := ConnectRabbitMQ(self.conf)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	self.conn = conn
    78  	return self, nil
    79  }
    80  
    81  func (self *PublishManager) openChannel() (*amqp.Channel, error) {
    82  	self.mu.Lock()
    83  	defer self.mu.Unlock()
    84  	channel, err := self.conn.Channel()
    85  	if err != nil {
    86  		e, b := err.(*amqp.Error)
    87  		if b && e.Code == 504 { // 重连connection
    88  			if _, err := self.Connect(); err != nil {
    89  				return nil, err
    90  			}
    91  		}
    92  		return nil, err
    93  	}
    94  	return channel, nil
    95  }
    96  
    97  func (self *PublishManager) getChannel() *amqp.Channel {
    98  	index := 0
    99  	for {
   100  		if index > 0 {
   101  			zlog.Warn("rabbitmq publish trying to connect again", 0, zlog.Int("tried", index))
   102  		}
   103  		channel, err := self.openChannel()
   104  		if err != nil {
   105  			zlog.Error("rabbitmq publish init Connection/Channel failed: ", 0, zlog.AddError(err))
   106  			time.Sleep(2500 * time.Millisecond)
   107  			index++
   108  			continue
   109  		}
   110  		return channel
   111  	}
   112  }
   113  
   114  func (self *PublishManager) Queue(data *MsgData) (*QueueData, error) {
   115  	pub, err := self.initQueue(data)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  	return &QueueData{
   120  		Name:      pub.queue.Name,
   121  		Consumers: pub.queue.Consumers,
   122  		Messages:  pub.queue.Messages}, nil
   123  }
   124  
   125  func (self *PublishManager) initQueue(data *MsgData) (*PublishMQ, error) {
   126  	if len(data.Option.Router) == 0 {
   127  		data.Option.Router = data.Option.Queue
   128  	}
   129  	if !utils.CheckInt(data.Option.SigTyp, 0, 1) {
   130  		data.Option.SigTyp = 1
   131  	}
   132  	if len(data.Option.SigKey) < 32 {
   133  		data.Option.SigKey = utils.AddStr(utils.GetLocalSecretKey(), self.conf.SecretKey)
   134  	}
   135  	if len(data.Nonce) == 0 {
   136  		data.Nonce = utils.RandStr(6)
   137  	}
   138  	chanKey := utils.AddStr(data.Option.Exchange, data.Option.Router, data.Option.Queue)
   139  	// 判断生成通道
   140  	pub, ok := self.channels[chanKey]
   141  	if ok {
   142  		return pub, nil
   143  	}
   144  	self.mu0.Lock()
   145  	defer self.mu0.Unlock()
   146  	pub, ok = self.channels[chanKey]
   147  	if !ok {
   148  		if len(data.Option.Kind) == 0 {
   149  			data.Option.Kind = direct
   150  		}
   151  		if len(data.Option.Router) == 0 {
   152  			data.Option.Router = data.Option.Queue
   153  		}
   154  		opt := &Option{
   155  			Kind:     data.Option.Kind,
   156  			Exchange: data.Option.Exchange,
   157  			Queue:    data.Option.Queue,
   158  			Router:   data.Option.Router,
   159  			SigTyp:   data.Option.SigTyp,
   160  		}
   161  		pub = &PublishMQ{channel: self.getChannel(), option: opt}
   162  		if err := pub.prepareExchange(); err != nil {
   163  			return nil, err
   164  		}
   165  		if err := pub.prepareQueue(); err != nil {
   166  			return nil, err
   167  		}
   168  		pub.ready = true
   169  		self.channels[chanKey] = pub
   170  		self.listen(pub)
   171  	}
   172  	return pub, nil
   173  }
   174  
   175  func (self *PublishManager) listen(pub *PublishMQ) {
   176  	pub.mu.Lock()
   177  	defer pub.mu.Unlock()
   178  	if !pub.ready { // 重新连接channel
   179  		pub.channel = self.getChannel()
   180  		pub.ready = true
   181  	}
   182  	closeChan := make(chan bool, 1)
   183  	go func(chan<- bool) {
   184  		mqErr := make(chan *amqp.Error)
   185  		closeErr := <-pub.channel.NotifyClose(mqErr)
   186  		zlog.Error("rabbitmq publish connection/channel receive failed", 0, zlog.String("exchange", pub.option.Exchange), zlog.String("queue", pub.option.Queue), zlog.AddError(closeErr))
   187  		closeChan <- true
   188  	}(closeChan)
   189  
   190  	go func(<-chan bool) {
   191  		for {
   192  			select {
   193  			case <-closeChan:
   194  				pub.ready = false
   195  				self.listen(pub)
   196  				zlog.Warn("rabbitmq publish received channel exception, successful reconnected", 0, zlog.String("exchange", pub.option.Exchange), zlog.String("queue", pub.option.Queue))
   197  				return
   198  			}
   199  		}
   200  	}(closeChan)
   201  }
   202  
   203  func (self *PublishManager) Publish(exchange, queue string, dataType int64, content interface{}) error {
   204  	msg := &MsgData{
   205  		Option: Option{
   206  			Exchange: exchange,
   207  			Queue:    queue,
   208  		},
   209  		Type:    dataType,
   210  		Content: content,
   211  	}
   212  	return self.PublishMsgData(msg)
   213  }
   214  
   215  func (self *PublishManager) PublishMsgData(data *MsgData) error {
   216  	if data == nil {
   217  		return utils.Error("publish data empty")
   218  	}
   219  	pub, err := self.initQueue(data)
   220  	if err != nil {
   221  		return err
   222  	}
   223  	// 数据加密模式
   224  	sigTyp := data.Option.SigTyp
   225  	sigKey := data.Option.SigKey
   226  	if len(sigKey) == 0 {
   227  		return utils.Error("rabbitmq publish data key is nil")
   228  	}
   229  	content, err := utils.ToJsonBase64(data.Content)
   230  	if err != nil {
   231  		return err
   232  	}
   233  	if len(content) == 0 {
   234  		return utils.Error("rabbitmq publish content is nil")
   235  	}
   236  	if sigTyp == 1 {
   237  		aesContent, err := utils.AesEncrypt(utils.Str2Bytes(content), sigKey, sigKey)
   238  		if err != nil {
   239  			return utils.Error("rabbitmq publish content aes encrypt failed: ", err)
   240  		}
   241  		content = aesContent
   242  	}
   243  	data.Content = content
   244  	data.Signature = utils.HMAC_SHA256(utils.AddStr(content, data.Nonce), sigKey, true)
   245  	data.Option.SigKey = ""
   246  	if _, err := pub.sendMessage(data); err != nil {
   247  		return err
   248  	}
   249  	return nil
   250  }
   251  
   252  func (self *PublishMQ) sendMessage(msg *MsgData) (bool, error) {
   253  	body, err := utils.JsonMarshal(msg)
   254  	if err != nil {
   255  		return false, err
   256  	}
   257  	data := amqp.Publishing{ContentType: "text/plain", Body: body}
   258  	if err := self.channel.Publish(self.option.Exchange, self.option.Router, false, false, data); err != nil {
   259  		return false, err
   260  	}
   261  	return true, nil
   262  }
   263  
   264  func (self *PublishMQ) prepareExchange() error {
   265  	zlog.Println(fmt.Sprintf("rabbitmq publish init [%s - %s] successful", self.option.Kind, self.option.Exchange))
   266  	return self.channel.ExchangeDeclare(self.option.Exchange, self.option.Kind, true, false, false, false, nil)
   267  }
   268  
   269  func (self *PublishMQ) prepareQueue() error {
   270  	if len(self.option.Queue) == 0 {
   271  		return nil
   272  	}
   273  	if q, err := self.channel.QueueDeclare(self.option.Queue, true, false, false, false, nil); err != nil {
   274  		return err
   275  	} else {
   276  		self.queue = &q
   277  	}
   278  	if err := self.channel.QueueBind(self.option.Queue, self.option.Router, self.option.Exchange, false, nil); err != nil {
   279  		return err
   280  	}
   281  	return nil
   282  }