func (c *Connector) dialWithIt(ctx context.Context, url string, config amqp.Config) error {
	var err error
	for i := uint(1); i <= c.cfg.ReconnectAttempts; i++ {
		c.conn, err = amqp.DialConfig(url, config)
		if err != nil {
			c.emitRetried(Retried{i, err})
			select {
			case <-ctx.Done():
				return ctx.Err()
			case <-time.After(c.cfg.Wait):
				continue
			}
		}
		return nil
	}
	return errors.WithMessage(err, "maximum attempts exceeded")
}
dialWithIt try to connect to RabbitMQ.
On error it will reconnect.
If maximum retry count exceeded error will return.
package rabbitroutine
import (
	"context"
	"math"
	"sync"
	"time"
	"github.com/pkg/errors"
	"github.com/streadway/amqp"
	"golang.org/x/sync/errgroup"
)
const (
	defaultHeartbeat = 10 * time.Second
	defaultLocale    = "en_US"
)
type Config struct {
	ReconnectAttempts uint
	Wait time.Duration
}
type Connector struct {
	cfg Config
	conn   *amqp.Connection
	connCh chan *amqp.Connection
	retries []func(Retried)
	dials []func(Dialed)
	amqpnotifies []func(AMQPNotified)
}
func NewConnector(cfg Config) *Connector {
	if cfg.ReconnectAttempts == 0 {
		cfg.ReconnectAttempts = math.MaxUint32
	}
	return &Connector{
		cfg:    cfg,
		connCh: make(chan *amqp.Connection),
	}
}
func (c *Connector) StartMultipleConsumers(ctx context.Context, consumer Consumer, count int) error {
	var lastErr error
	for {
		if contextDone(ctx) {
			return lastErr
		}
		if lastErr != nil {
			select {
			case <-ctx.Done():
				return ctx.Err()
			case <-time.After(c.cfg.Wait):
			}
		}
		declareChannel, err := c.Channel(ctx)
		if err != nil {
			lastErr = errors.WithMessage(err, "failed to get channel")
			continue
		}
		err = consumer.Declare(ctx, declareChannel)
		if err != nil {
			lastErr = errors.WithMessage(err, "failed to declare consumer")
			continue
		}
		err = declareChannel.Close()
		if err != nil {
			lastErr = errors.WithMessage(err, "failed to close declareChannel")
			continue
		}
		var g errgroup.Group
		consumeCtx, cancel := context.WithCancel(ctx)
		for i := 0; i < count; i++ {
			consumeChannel, err := c.Channel(consumeCtx)
			if err != nil {
				cancel()
				break
			}
			var once sync.Once
			closeCh := consumeChannel.NotifyClose(make(chan *amqp.Error, 1))
			g.Go(func() error {
				defer cancel()
				err := consumer.Consume(consumeCtx, consumeChannel)
				if err != nil {
					return errors.Wrap(err, "failed to consume")
				}
				var closeErr error
				once.Do(func() {
					closeErr = consumeChannel.Close()
				})
				if closeErr != nil && closeErr != amqp.ErrClosed {
					return errors.Wrap(closeErr, "failed to close amqp channel")
				}
				return nil
			})
			g.Go(func() error {
				defer cancel()
				var stopErr error
				select {
				case <-consumeCtx.Done():
					stopErr = consumeCtx.Err()
				case amqpErr := <-closeCh:
					c.emitAMQPNotified(AMQPNotified{amqpErr})
					stopErr = amqpErr
				}
				var closeErr error
				once.Do(func() {
					closeErr = consumeChannel.Close()
				})
				if closeErr != nil && closeErr != amqp.ErrClosed {
					return errors.Wrap(closeErr, "failed to close amqp channel")
				}
				return stopErr
			})
		}
		lastErr = g.Wait()
		cancel()
	}
}
func (c *Connector) StartConsumer(ctx context.Context, consumer Consumer) error {
	return c.StartMultipleConsumers(ctx, consumer, 1)
}
func (c *Connector) Channel(ctx context.Context) (*amqp.Channel, error) {
	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	case conn := <-c.connCh:
		return conn.Channel()
	}
}
func (c *Connector) AddRetriedListener(h func(Retried)) {
	c.retries = append(c.retries, h)
}
func (c *Connector) emitRetried(r Retried) {
	for _, h := range c.retries {
		h(r)
	}
}
func (c *Connector) AddDialedListener(h func(r Dialed)) {
	c.dials = append(c.dials, h)
}
func (c *Connector) emitDialed(d Dialed) {
	for _, h := range c.dials {
		h(d)
	}
}
func (c *Connector) AddAMQPNotifiedListener(h func(n AMQPNotified)) {
	c.amqpnotifies = append(c.amqpnotifies, h)
}
func (c *Connector) emitAMQPNotified(n AMQPNotified) {
	for _, h := range c.amqpnotifies {
		h(n)
	}
}