github.com/godaddy-x/freego@v1.0.156/amqp/pull.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  	pullMgrs = make(map[string]*PullManager)
    15  )
    16  
    17  type PullManager struct {
    18  	mu        sync.Mutex
    19  	conf      AmqpConfig
    20  	conn      *amqp.Connection
    21  	receivers []*PullReceiver
    22  }
    23  
    24  func (self *PullManager) InitConfig(input ...AmqpConfig) (*PullManager, error) {
    25  	for _, v := range input {
    26  		if _, b := pullMgrs[v.DsName]; b {
    27  			return nil, utils.Error("rabbitmq pull init failed: [", v.DsName, "] exist")
    28  		}
    29  		if len(v.DsName) == 0 {
    30  			v.DsName = DIC.MASTER
    31  		}
    32  		pullMgr := &PullManager{
    33  			conf:      v,
    34  			receivers: make([]*PullReceiver, 0),
    35  		}
    36  		if _, err := pullMgr.Connect(); err != nil {
    37  			return nil, err
    38  		}
    39  		pullMgrs[v.DsName] = pullMgr
    40  		zlog.Printf("rabbitmq pull service【%s】has been started successful", v.DsName)
    41  	}
    42  	return self, nil
    43  }
    44  
    45  func (self *PullManager) Client(ds ...string) (*PullManager, error) {
    46  	dsName := DIC.MASTER
    47  	if len(ds) > 0 && len(ds[0]) > 0 {
    48  		dsName = ds[0]
    49  	}
    50  	return pullMgrs[dsName], nil
    51  }
    52  
    53  func NewPull(ds ...string) (*PullManager, error) {
    54  	return new(PullManager).Client(ds...)
    55  }
    56  
    57  func (self *PullManager) AddPullReceiver(receivers ...*PullReceiver) {
    58  	for _, v := range receivers {
    59  		go self.start(v)
    60  	}
    61  }
    62  
    63  func (self *PullManager) start(receiver *PullReceiver) {
    64  	self.receivers = append(self.receivers, receiver)
    65  	self.listen(receiver)
    66  	time.Sleep(100 * time.Millisecond)
    67  }
    68  
    69  func (self *PullManager) Connect() (*PullManager, error) {
    70  	conn, err := ConnectRabbitMQ(self.conf)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	self.conn = conn
    75  	return self, nil
    76  }
    77  
    78  func (self *PullManager) openChannel() (*amqp.Channel, error) {
    79  	self.mu.Lock()
    80  	defer self.mu.Unlock()
    81  	channel, err := self.conn.Channel()
    82  	if err != nil {
    83  		e, b := err.(*amqp.Error)
    84  		if b && e.Code == 504 { // 重连connection
    85  			if _, err := self.Connect(); err != nil {
    86  				return nil, err
    87  			}
    88  		}
    89  		return nil, err
    90  	}
    91  	return channel, nil
    92  }
    93  
    94  func (self *PullManager) getChannel() *amqp.Channel {
    95  	index := 0
    96  	for {
    97  		if index > 0 {
    98  			zlog.Warn("rabbitmq pull trying to connect again", 0, zlog.Int("tried", index))
    99  		}
   100  		channel, err := self.openChannel()
   101  		if err != nil {
   102  			zlog.Error("rabbitmq pull init Connection/Channel failed", 0, zlog.AddError(err))
   103  			time.Sleep(2500 * time.Millisecond)
   104  			index++
   105  			continue
   106  		}
   107  		return channel
   108  	}
   109  }
   110  
   111  func (self *PullManager) listen(receiver *PullReceiver) {
   112  	channel := self.getChannel()
   113  	receiver.channel = channel
   114  	exchange := receiver.Config.Option.Exchange
   115  	queue := receiver.Config.Option.Queue
   116  	kind := receiver.Config.Option.Kind
   117  	router := receiver.Config.Option.Router
   118  	prefetchCount := receiver.Config.PrefetchCount
   119  	prefetchSize := receiver.Config.PrefetchSize
   120  	if !utils.CheckInt(receiver.Config.Option.SigTyp, 0, 1) {
   121  		receiver.Config.Option.SigTyp = 1
   122  	}
   123  	if len(receiver.Config.Option.SigKey) < 32 {
   124  		receiver.Config.Option.SigKey = utils.AddStr(utils.GetLocalSecretKey(), self.conf.SecretKey)
   125  	}
   126  	if len(kind) == 0 {
   127  		kind = direct
   128  	}
   129  	if len(router) == 0 {
   130  		router = queue
   131  	}
   132  	if prefetchCount == 0 {
   133  		prefetchCount = 1
   134  	}
   135  	zlog.Println(fmt.Sprintf("rabbitmq pull init queue [%s - %s - %s - %s] successful...", kind, exchange, router, queue))
   136  	if err := self.prepareExchange(channel, exchange, kind); err != nil {
   137  		receiver.OnError(fmt.Errorf("rabbitmq pull init exchange [%s] failed: %s", exchange, err.Error()))
   138  		return
   139  	}
   140  	if err := self.prepareQueue(channel, exchange, queue, router); err != nil {
   141  		receiver.OnError(fmt.Errorf("rabbitmq pull bind queue [%s] to exchange [%s] failed: %s", queue, exchange, err.Error()))
   142  		return
   143  	}
   144  	if err := channel.Qos(prefetchCount, prefetchSize, false); err != nil {
   145  		receiver.OnError(fmt.Errorf("rabbitmq pull queue %s qos failed failed: %s", queue, err.Error()))
   146  	}
   147  	// 开启消费数据
   148  	msgs, err := channel.Consume(queue, "", false, false, false, false, nil)
   149  	if err != nil {
   150  		receiver.OnError(fmt.Errorf("rabbitmq pull get queue %s failed: %s", queue, err.Error()))
   151  	}
   152  	closeChan := make(chan bool, 1)
   153  	go func(chan<- bool) {
   154  		mqErr := make(chan *amqp.Error)
   155  		closeErr := <-channel.NotifyClose(mqErr)
   156  		zlog.Error("rabbitmq pull connection/channel receive failed", 0, zlog.String("exchange", exchange), zlog.String("queue", queue), zlog.AddError(closeErr))
   157  		closeChan <- true
   158  	}(closeChan)
   159  
   160  	go func(<-chan bool) {
   161  		for {
   162  			select {
   163  			case d := <-msgs:
   164  				for !receiver.OnReceive(d.Body) {
   165  					delay := receiver.Delay
   166  					if delay == 0 {
   167  						delay = 5
   168  					}
   169  					time.Sleep(time.Duration(delay) * time.Second)
   170  				}
   171  				if err := d.Ack(false); err != nil {
   172  					zlog.Error("rabbitmq pull received ack failed", 0, zlog.AddError(err))
   173  				}
   174  			case <-closeChan:
   175  				self.listen(receiver)
   176  				zlog.Warn("rabbitmq pull received channel exception, successful reconnected", 0, zlog.String("exchange", exchange), zlog.String("queue", queue))
   177  				return
   178  			}
   179  		}
   180  	}(closeChan)
   181  }
   182  
   183  func (self *PullManager) prepareExchange(channel *amqp.Channel, exchange, kind string) error {
   184  	return channel.ExchangeDeclare(exchange, kind, true, false, false, false, nil)
   185  }
   186  
   187  func (self *PullManager) prepareQueue(channel *amqp.Channel, exchange, queue, router string) error {
   188  	if _, err := channel.QueueDeclare(queue, true, false, false, false, nil); err != nil {
   189  		return err
   190  	}
   191  	if err := channel.QueueBind(queue, router, exchange, false, nil); err != nil {
   192  		return err
   193  	}
   194  	return nil
   195  }
   196  
   197  func (self *PullReceiver) OnError(err error) {
   198  	zlog.Error("rabbitmq pull receiver data failed", 0, zlog.AddError(err))
   199  }
   200  
   201  type PullReceiver struct {
   202  	channel      *amqp.Channel
   203  	Config       *Config
   204  	ContentInter func(typ int64) interface{}
   205  	Callback     func(msg *MsgData) error
   206  	Debug        bool // 是否打印具体pull数据实体
   207  	Delay        int  // pull失败重试间隔
   208  }
   209  
   210  func (self *PullReceiver) OnReceive(b []byte) bool {
   211  	if b == nil || len(b) == 0 || string(b) == "{}" || string(b) == "[]" {
   212  		return true
   213  	}
   214  	if self.Debug {
   215  		defer zlog.Debug("rabbitmq pull consumption data monitoring", utils.UnixMilli(), zlog.String("message", utils.Bytes2Str(b)))
   216  	}
   217  	msg := &MsgData{}
   218  	if err := utils.JsonUnmarshal(b, msg); err != nil {
   219  		zlog.Error("rabbitmq pull consumption data parsing failed", 0, zlog.Any("option", self.Config.Option), zlog.Any("message", msg), zlog.AddError(err))
   220  	}
   221  	if msg.Content == nil {
   222  		return true
   223  	}
   224  	sigTyp := self.Config.Option.SigTyp
   225  	sigKey := self.Config.Option.SigKey
   226  
   227  	if len(msg.Signature) == 0 {
   228  		zlog.Error("rabbitmq pull consumption data signature is nil", 0, zlog.Any("option", self.Config.Option), zlog.Any("message", msg))
   229  		return true
   230  	}
   231  	v, ok := msg.Content.(string)
   232  	if !ok {
   233  		zlog.Error("rabbitmq consumption data (non string type)", 0, zlog.Any("option", self.Config.Option), zlog.Any("message", msg))
   234  		return true
   235  	}
   236  	if len(v) == 0 {
   237  		zlog.Error("rabbitmq consumption data is nil", 0, zlog.Any("option", self.Config.Option), zlog.Any("message", msg))
   238  		return true
   239  	}
   240  	if msg.Signature != utils.HMAC_SHA256(utils.AddStr(v, msg.Nonce), sigKey, true) {
   241  		zlog.Error("rabbitmq consumption data signature invalid", 0, zlog.Any("option", self.Config.Option), zlog.Any("message", msg))
   242  		return true
   243  	}
   244  	if sigTyp == 1 {
   245  		aesContent, err := utils.AesDecrypt(v, sigKey, sigKey)
   246  		if err != nil {
   247  			zlog.Error("rabbitmq consumption data aes decrypt failed", 0, zlog.Any("option", self.Config.Option), zlog.Any("message", msg))
   248  			return true
   249  		}
   250  		v = utils.Bytes2Str(aesContent)
   251  	}
   252  	btv := utils.Base64Decode(v)
   253  	if btv == nil || len(btv) == 0 {
   254  		zlog.Error("rabbitmq pull consumption data Base64 parsing failed", 0, zlog.Any("option", self.Config.Option), zlog.Any("message", msg))
   255  		return true
   256  	}
   257  	if self.ContentInter == nil {
   258  		content := map[string]interface{}{}
   259  		if err := utils.JsonUnmarshal(btv, &content); err != nil {
   260  			zlog.Error("rabbitmq pull consumption data conversion type(Map) failed", 0, zlog.Any("option", self.Config.Option), zlog.Any("message", msg), zlog.AddError(err))
   261  			return true
   262  		}
   263  		msg.Content = content
   264  	} else {
   265  		content := self.ContentInter(msg.Type)
   266  		if err := utils.JsonUnmarshal(btv, content); err != nil {
   267  			zlog.Error("rabbitmq pull consumption data conversion type(ContentInter) failed", 0, zlog.Any("option", self.Config.Option), zlog.Any("message", msg), zlog.AddError(err))
   268  			return true
   269  		}
   270  		msg.Content = content
   271  	}
   272  
   273  	if err := self.Callback(msg); err != nil {
   274  		if self.Debug {
   275  			zlog.Error("rabbitmq pull consumption data processing failed", 0, zlog.Any("option", self.Config.Option), zlog.Any("message", msg), zlog.AddError(err))
   276  		} else {
   277  			zlog.Error("rabbitmq pull consumption data processing failed", 0, zlog.Any("option", self.Config.Option), zlog.AddError(err))
   278  		}
   279  		if self.Config.IsNack {
   280  			return false
   281  		}
   282  	}
   283  	return true
   284  }