package rabbitmq import ( "context" "time" "git.wishpal.cn/wishpal_ironfan/xframe/base/proc" "git.wishpal.cn/wishpal_ironfan/xframe/base/rescue" "git.wishpal.cn/wishpal_ironfan/xframe/base/threading" "git.wishpal.cn/wishpal_ironfan/xframe/component/logger" "github.com/pkg/errors" amqp "github.com/rabbitmq/amqp091-go" ) type RabbitSender struct { conf RabbitSenderConf conn *amqp.Connection connError chan *amqp.Error channelPool chan *amqp.Channel closedPool bool //used for putChannel, avoid triggering panic when writing data to closed channels stopCtx context.Context // control the lifecycle of get_channel and reconnect stopCtxCancel context.CancelFunc } func NewSender(conf RabbitSenderConf) (*RabbitSender, error) { ctx, cancel := context.WithCancel(context.Background()) sender := &RabbitSender{ conf: conf, stopCtx: ctx, stopCtxCancel: cancel, } // start sender if err := sender.start(); err != nil { return nil, err } // watch reconnect threading.GoSafeVoid(sender.watchReconnect) // watch channel pool size4 threading.GoSafeVoid(func() { for { logger.Infof("rabbitmq sender channel pool(%d).", len(sender.channelPool)) time.Sleep(1 * time.Second) } }) // quit proc.AddShutdownListener(func() { cancel() sender.cleanup() }) return sender, nil } func (q *RabbitSender) start() error { // connect to rabbitmq conn, err := amqp.Dial(q.conf.URL) if err != nil { return err } defer func() { if err != nil && conn != nil { conn.Close() } }() // declares an exchange on the server channel, err := conn.Channel() if err != nil { return err } err = channel.ExchangeDeclare( q.conf.Exchange.Name, q.conf.Exchange.Type, q.conf.Exchange.Durable, q.conf.Exchange.AutoDelete, q.conf.Exchange.Internal, q.conf.Exchange.NoWait, nil, ) if err != nil { return err } // start multiple publisher channels, process message publishing pool := make(chan *amqp.Channel, q.conf.Concurrency) for i := 0; i < q.conf.Concurrency; i++ { channel, err = conn.Channel() if err != nil { return err } pool <- channel } q.conn = conn q.channelPool = pool q.connError = make(chan *amqp.Error, 1) q.conn.NotifyClose(q.connError) q.closedPool = false return nil } func (q *RabbitSender) reconnect() { var ( retry = 0 maxRetry = 8 ) for { backoffDuration := time.Duration(1< maxRetry { backoffDuration = time.Duration(1<