| @ -1,134 +0,0 @@ | |||||
| package rabbitmq | |||||
| import ( | |||||
| "errors" | |||||
| "sync" | |||||
| "time" | |||||
| amqp "github.com/rabbitmq/amqp091-go" | |||||
| ) | |||||
| type channelManager struct { | |||||
| logger Logger | |||||
| url string | |||||
| channel *amqp.Channel | |||||
| connection *amqp.Connection | |||||
| amqpConfig Config | |||||
| channelMux *sync.RWMutex | |||||
| notifyCancelOrClose chan error | |||||
| reconnectInterval time.Duration | |||||
| reconnectionCount uint | |||||
| } | |||||
| func newChannelManager(url string, conf Config, log Logger, reconnectInterval time.Duration) (*channelManager, error) { | |||||
| conn, ch, err := getNewChannel(url, conf) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| chManager := channelManager{ | |||||
| logger: log, | |||||
| url: url, | |||||
| connection: conn, | |||||
| channel: ch, | |||||
| channelMux: &sync.RWMutex{}, | |||||
| amqpConfig: conf, | |||||
| notifyCancelOrClose: make(chan error), | |||||
| reconnectInterval: reconnectInterval, | |||||
| } | |||||
| go chManager.startNotifyCancelOrClosed() | |||||
| return &chManager, nil | |||||
| } | |||||
| func getNewChannel(url string, conf Config) (*amqp.Connection, *amqp.Channel, error) { | |||||
| amqpConn, err := amqp.DialConfig(url, amqp.Config(conf)) | |||||
| if err != nil { | |||||
| return nil, nil, err | |||||
| } | |||||
| ch, err := amqpConn.Channel() | |||||
| if err != nil { | |||||
| return nil, nil, err | |||||
| } | |||||
| return amqpConn, ch, nil | |||||
| } | |||||
| // startNotifyCancelOrClosed listens on the channel's cancelled and closed | |||||
| // notifiers. When it detects a problem, it attempts to reconnect. | |||||
| // Once reconnected, it sends an error back on the manager's notifyCancelOrClose | |||||
| // channel | |||||
| func (chManager *channelManager) startNotifyCancelOrClosed() { | |||||
| notifyCloseChan := chManager.channel.NotifyClose(make(chan *amqp.Error, 1)) | |||||
| notifyCancelChan := chManager.channel.NotifyCancel(make(chan string, 1)) | |||||
| select { | |||||
| case err := <-notifyCloseChan: | |||||
| if err != nil { | |||||
| chManager.logger.Errorf("attempting to reconnect to amqp server after close with error: %v", err) | |||||
| chManager.reconnectLoop() | |||||
| chManager.logger.Warnf("successfully reconnected to amqp server") | |||||
| chManager.notifyCancelOrClose <- err | |||||
| } | |||||
| if err == nil { | |||||
| chManager.logger.Infof("amqp channel closed gracefully") | |||||
| } | |||||
| case err := <-notifyCancelChan: | |||||
| chManager.logger.Errorf("attempting to reconnect to amqp server after cancel with error: %s", err) | |||||
| chManager.reconnectLoop() | |||||
| chManager.logger.Warnf("successfully reconnected to amqp server after cancel") | |||||
| chManager.notifyCancelOrClose <- errors.New(err) | |||||
| } | |||||
| } | |||||
| // reconnectLoop continuously attempts to reconnect | |||||
| func (chManager *channelManager) reconnectLoop() { | |||||
| for { | |||||
| chManager.logger.Infof("waiting %s seconds to attempt to reconnect to amqp server", chManager.reconnectInterval) | |||||
| time.Sleep(chManager.reconnectInterval) | |||||
| err := chManager.reconnect() | |||||
| if err != nil { | |||||
| chManager.logger.Errorf("error reconnecting to amqp server: %v", err) | |||||
| } else { | |||||
| chManager.reconnectionCount++ | |||||
| go chManager.startNotifyCancelOrClosed() | |||||
| return | |||||
| } | |||||
| } | |||||
| } | |||||
| // reconnect safely closes the current channel and obtains a new one | |||||
| func (chManager *channelManager) reconnect() error { | |||||
| chManager.channelMux.Lock() | |||||
| defer chManager.channelMux.Unlock() | |||||
| newConn, newChannel, err := getNewChannel(chManager.url, chManager.amqpConfig) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| if err = chManager.channel.Close(); err != nil { | |||||
| chManager.logger.Warnf("error closing channel while reconnecting: %v", err) | |||||
| } | |||||
| if err = chManager.connection.Close(); err != nil { | |||||
| chManager.logger.Warnf("error closing connection while reconnecting: %v", err) | |||||
| } | |||||
| chManager.connection = newConn | |||||
| chManager.channel = newChannel | |||||
| return nil | |||||
| } | |||||
| // close safely closes the current channel and connection | |||||
| func (chManager *channelManager) close() error { | |||||
| chManager.channelMux.Lock() | |||||
| defer chManager.channelMux.Unlock() | |||||
| err := chManager.channel.Close() | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| err = chManager.connection.Close() | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @ -1,9 +0,0 @@ | |||||
| package rabbitmq | |||||
| import amqp "github.com/rabbitmq/amqp091-go" | |||||
| // Config wraps amqp.Config | |||||
| // Config is used in DialConfig and Open to specify the desired tuning | |||||
| // parameters used during a connection open handshake. The negotiated tuning | |||||
| // will be stored in the returned connection's Config field. | |||||
| type Config amqp.Config | |||||
| @ -0,0 +1,110 @@ | |||||
| package rabbitmq | |||||
| import ( | |||||
| amqp "github.com/rabbitmq/amqp091-go" | |||||
| "github.com/wagslane/go-rabbitmq/internal/connectionmanager" | |||||
| ) | |||||
| // Conn manages the connection to a rabbit cluster | |||||
| // it is intended to be shared across publishers and consumers | |||||
| type Conn struct { | |||||
| connectionManager *connectionmanager.ConnectionManager | |||||
| reconnectErrCh <-chan error | |||||
| closeConnectionToManagerCh chan<- struct{} | |||||
| notifyReturnChan chan Return | |||||
| notifyPublishChan chan Confirmation | |||||
| options ConnectionOptions | |||||
| } | |||||
| // Config wraps amqp.Config | |||||
| // Config is used in DialConfig and Open to specify the desired tuning | |||||
| // parameters used during a connection open handshake. The negotiated tuning | |||||
| // will be stored in the returned connection's Config field. | |||||
| type Config amqp.Config | |||||
| // NewConn creates a new connection manager | |||||
| func NewConn(url string, optionFuncs ...func(*ConnectionOptions)) (*Conn, error) { | |||||
| defaultOptions := getDefaultConnectionOptions() | |||||
| options := &defaultOptions | |||||
| for _, optionFunc := range optionFuncs { | |||||
| optionFunc(options) | |||||
| } | |||||
| manager, err := connectionmanager.NewConnectionManager(url, amqp.Config(options.Config), options.Logger, options.ReconnectInterval) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| err = manager.QosSafe( | |||||
| options.QOSPrefetch, | |||||
| 0, | |||||
| options.QOSGlobal, | |||||
| ) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| reconnectErrCh, closeCh := manager.NotifyReconnect() | |||||
| conn := &Conn{ | |||||
| connectionManager: manager, | |||||
| reconnectErrCh: reconnectErrCh, | |||||
| closeConnectionToManagerCh: closeCh, | |||||
| notifyReturnChan: nil, | |||||
| notifyPublishChan: nil, | |||||
| options: *options, | |||||
| } | |||||
| go conn.handleRestarts() | |||||
| return conn, nil | |||||
| } | |||||
| func (conn *Conn) handleRestarts() { | |||||
| for err := range conn.reconnectErrCh { | |||||
| conn.options.Logger.Infof("successful connection recovery from: %v", err) | |||||
| go conn.startNotifyReturnHandler() | |||||
| go conn.startNotifyPublishHandler() | |||||
| } | |||||
| } | |||||
| func (conn *Conn) startNotifyReturnHandler() { | |||||
| if conn.notifyReturnChan == nil { | |||||
| return | |||||
| } | |||||
| returnAMQPCh := conn.connectionManager.NotifyReturnSafe(make(chan amqp.Return, 1)) | |||||
| for ret := range returnAMQPCh { | |||||
| conn.notifyReturnChan <- Return{ret} | |||||
| } | |||||
| } | |||||
| func (conn *Conn) startNotifyPublishHandler() { | |||||
| if conn.notifyPublishChan == nil { | |||||
| return | |||||
| } | |||||
| conn.connectionManager.ConfirmSafe(false) | |||||
| publishAMQPCh := conn.connectionManager.NotifyPublishSafe(make(chan amqp.Confirmation, 1)) | |||||
| for conf := range publishAMQPCh { | |||||
| conn.notifyPublishChan <- Confirmation{ | |||||
| Confirmation: conf, | |||||
| ReconnectionCount: int(conn.connectionManager.GetReconnectionCount()), | |||||
| } | |||||
| } | |||||
| } | |||||
| // NotifyReturn registers a listener for basic.return methods. | |||||
| // These can be sent from the server when a publish is undeliverable either from the mandatory or immediate flags. | |||||
| // These notifications are shared across an entire connection, so if you're creating multiple | |||||
| // publishers on the same connection keep that in mind | |||||
| func (conn *Conn) NotifyReturn() <-chan Return { | |||||
| conn.notifyReturnChan = make(chan Return) | |||||
| go conn.startNotifyReturnHandler() | |||||
| return conn.notifyReturnChan | |||||
| } | |||||
| // NotifyPublish registers a listener for publish confirmations, must set ConfirmPublishings option | |||||
| // These notifications are shared across an entire connection, so if you're creating multiple | |||||
| // publishers on the same connection keep that in mind | |||||
| func (conn *Conn) NotifyPublish() <-chan Confirmation { | |||||
| conn.notifyPublishChan = make(chan Confirmation) | |||||
| go conn.startNotifyPublishHandler() | |||||
| return conn.notifyPublishChan | |||||
| } | |||||
| @ -0,0 +1,67 @@ | |||||
| package rabbitmq | |||||
| import "time" | |||||
| // ConnectionOptions are used to describe how a new consumer will be created. | |||||
| type ConnectionOptions struct { | |||||
| QOSPrefetch int | |||||
| QOSGlobal bool | |||||
| ReconnectInterval time.Duration | |||||
| Logger Logger | |||||
| Config Config | |||||
| } | |||||
| // getDefaultConnectionOptions describes the options that will be used when a value isn't provided | |||||
| func getDefaultConnectionOptions() ConnectionOptions { | |||||
| return ConnectionOptions{ | |||||
| QOSPrefetch: 0, | |||||
| QOSGlobal: false, | |||||
| ReconnectInterval: time.Second * 5, | |||||
| Logger: stdDebugLogger{}, | |||||
| Config: Config{}, | |||||
| } | |||||
| } | |||||
| // WithConnectionOptionsQOSPrefetch returns a function that sets the prefetch count, which means that | |||||
| // many messages will be fetched from the server in advance to help with throughput. | |||||
| // This doesn't affect the handler, messages are still processed one at a time. | |||||
| func WithConnectionOptionsQOSPrefetch(prefetchCount int) func(*ConnectionOptions) { | |||||
| return func(options *ConnectionOptions) { | |||||
| options.QOSPrefetch = prefetchCount | |||||
| } | |||||
| } | |||||
| // WithConnectionOptionsQOSGlobal sets the qos on the channel to global, which means | |||||
| // these QOS settings apply to ALL existing and future | |||||
| // consumers on all channels on the same connection | |||||
| func WithConnectionOptionsQOSGlobal(options *ConnectionOptions) { | |||||
| options.QOSGlobal = true | |||||
| } | |||||
| // WithConnectionOptionsReconnectInterval sets the reconnection interval | |||||
| func WithConnectionOptionsReconnectInterval(interval time.Duration) func(options *ConnectionOptions) { | |||||
| return func(options *ConnectionOptions) { | |||||
| options.ReconnectInterval = interval | |||||
| } | |||||
| } | |||||
| // WithConnectionOptionsLogging sets logging to true on the consumer options | |||||
| // and sets the | |||||
| func WithConnectionOptionsLogging(options *ConnectionOptions) { | |||||
| options.Logger = stdDebugLogger{} | |||||
| } | |||||
| // WithConnectionOptionsLogger sets logging to true on the consumer options | |||||
| // and sets the | |||||
| func WithConnectionOptionsLogger(log Logger) func(options *ConnectionOptions) { | |||||
| return func(options *ConnectionOptions) { | |||||
| options.Logger = log | |||||
| } | |||||
| } | |||||
| // WithConnectionOptionsConfig sets the Config used in the connection | |||||
| func WithConnectionOptionsConfig(cfg Config) func(options *ConnectionOptions) { | |||||
| return func(options *ConnectionOptions) { | |||||
| options.Config = cfg | |||||
| } | |||||
| } | |||||
| @ -1,373 +1,90 @@ | |||||
| package rabbitmq | package rabbitmq | ||||
| import "fmt" | |||||
| // DeclareOptions are used to describe how a new queues, exchanges the routing setup should look like. | |||||
| type DeclareOptions struct { | |||||
| Queue *QueueOptions | |||||
| Exchange *ExchangeOptions | |||||
| Bindings []Binding | |||||
| } | |||||
| // QueueOptions are used to configure a queue. | |||||
| // If the Passive flag is set the client will only check if the queue exists on the server | |||||
| // and that the settings match, no creation attempt will be made. | |||||
| type QueueOptions struct { | |||||
| Name string | |||||
| Durable bool | |||||
| AutoDelete bool | |||||
| Exclusive bool | |||||
| NoWait bool | |||||
| Passive bool // if false, a missing queue will be created on the server | |||||
| Args Table | |||||
| } | |||||
| // ExchangeOptions are used to configure an exchange. | |||||
| // If the Passive flag is set the client will only check if the exchange exists on the server | |||||
| // and that the settings match, no creation attempt will be made. | |||||
| type ExchangeOptions struct { | |||||
| Name string | |||||
| Kind string // possible values: empty string for default exchange or direct, topic, fanout | |||||
| Durable bool | |||||
| AutoDelete bool | |||||
| Internal bool | |||||
| NoWait bool | |||||
| Passive bool // if false, a missing exchange will be created on the server | |||||
| Args Table | |||||
| } | |||||
| // BindingOption are used to configure a queue bindings. | |||||
| type BindingOption struct { | |||||
| NoWait bool | |||||
| Args Table | |||||
| } | |||||
| // Binding describes a queue binding to a specific exchange. | |||||
| type Binding struct { | |||||
| BindingOption | |||||
| QueueName string | |||||
| ExchangeName string | |||||
| RoutingKey string | |||||
| } | |||||
| // SetBindings trys to generate bindings for the given routing keys and the queue and exchange options. | |||||
| // If either Queue or Exchange properties are empty or no queue name is specified, no bindings will be set. | |||||
| func (o *DeclareOptions) SetBindings(routingKeys []string, opt BindingOption) { | |||||
| if o.Queue == nil || o.Exchange == nil { | |||||
| return // nothing to set... | |||||
| } | |||||
| if o.Queue.Name == "" { | |||||
| return // nothing to set... | |||||
| } | |||||
| for _, routingKey := range routingKeys { | |||||
| o.Bindings = append(o.Bindings, Binding{ | |||||
| QueueName: o.Queue.Name, | |||||
| ExchangeName: o.Exchange.Name, | |||||
| RoutingKey: routingKey, | |||||
| BindingOption: opt, | |||||
| }) | |||||
| } | |||||
| } | |||||
| // handleDeclare handles the queue, exchange and binding declare process on the server. | |||||
| // If there are no options set, no actions will be executed. | |||||
| func handleDeclare(chManager *channelManager, options DeclareOptions) error { | |||||
| chManager.channelMux.RLock() | |||||
| defer chManager.channelMux.RUnlock() | |||||
| // bind queue | |||||
| if options.Queue != nil { | |||||
| queue := options.Queue | |||||
| if queue.Name == "" { | |||||
| return fmt.Errorf("missing queue name") | |||||
| } | |||||
| if queue.Passive { | |||||
| _, err := chManager.channel.QueueDeclarePassive( | |||||
| queue.Name, | |||||
| queue.Durable, | |||||
| queue.AutoDelete, | |||||
| queue.Exclusive, | |||||
| queue.NoWait, | |||||
| tableToAMQPTable(queue.Args), | |||||
| ) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } else { | |||||
| _, err := chManager.channel.QueueDeclare( | |||||
| queue.Name, | |||||
| queue.Durable, | |||||
| queue.AutoDelete, | |||||
| queue.Exclusive, | |||||
| queue.NoWait, | |||||
| tableToAMQPTable(queue.Args), | |||||
| ) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| } | |||||
| // bind exchange | |||||
| if options.Exchange != nil { | |||||
| exchange := options.Exchange | |||||
| if exchange.Name == "" { | |||||
| return fmt.Errorf("missing exchange name") | |||||
| } | |||||
| if exchange.Passive { | |||||
| err := chManager.channel.ExchangeDeclarePassive( | |||||
| exchange.Name, | |||||
| exchange.Kind, | |||||
| exchange.Durable, | |||||
| exchange.AutoDelete, | |||||
| exchange.Internal, | |||||
| exchange.NoWait, | |||||
| tableToAMQPTable(exchange.Args), | |||||
| ) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } else { | |||||
| err := chManager.channel.ExchangeDeclare( | |||||
| exchange.Name, | |||||
| exchange.Kind, | |||||
| exchange.Durable, | |||||
| exchange.AutoDelete, | |||||
| exchange.Internal, | |||||
| exchange.NoWait, | |||||
| tableToAMQPTable(exchange.Args), | |||||
| ) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| } | |||||
| // handle binding of queues to exchange | |||||
| for _, binding := range options.Bindings { | |||||
| err := chManager.channel.QueueBind( | |||||
| binding.QueueName, // name of the queue | |||||
| binding.RoutingKey, // bindingKey | |||||
| binding.ExchangeName, // sourceExchange | |||||
| binding.NoWait, // noWait | |||||
| tableToAMQPTable(binding.Args), // arguments | |||||
| import ( | |||||
| "github.com/wagslane/go-rabbitmq/internal/connectionmanager" | |||||
| ) | |||||
| func declareQueue(connManager *connectionmanager.ConnectionManager, options QueueOptions) error { | |||||
| if !options.Declare { | |||||
| return nil | |||||
| } | |||||
| if options.Passive { | |||||
| _, err := connManager.QueueDeclarePassiveSafe( | |||||
| options.Name, | |||||
| options.Durable, | |||||
| options.AutoDelete, | |||||
| options.Exclusive, | |||||
| options.NoWait, | |||||
| tableToAMQPTable(options.Args), | |||||
| ) | ) | ||||
| if err != nil { | if err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| return nil | |||||
| } | |||||
| _, err := connManager.QueueDeclareSafe( | |||||
| options.Name, | |||||
| options.Durable, | |||||
| options.AutoDelete, | |||||
| options.Exclusive, | |||||
| options.NoWait, | |||||
| tableToAMQPTable(options.Args), | |||||
| ) | |||||
| if err != nil { | |||||
| return err | |||||
| } | } | ||||
| return nil | return nil | ||||
| } | } | ||||
| // getExchangeOptionsOrSetDefault returns pointer to current ExchangeOptions options. | |||||
| // If no exchange options are set yet, new options with default values will be defined. | |||||
| func getExchangeOptionsOrSetDefault(options *DeclareOptions) *ExchangeOptions { | |||||
| if options.Exchange == nil { | |||||
| options.Exchange = &ExchangeOptions{ | |||||
| Name: "", | |||||
| Kind: "direct", | |||||
| Durable: false, | |||||
| AutoDelete: false, | |||||
| Internal: false, | |||||
| NoWait: false, | |||||
| Args: nil, | |||||
| Passive: false, | |||||
| } | |||||
| func declareExchange(connManager *connectionmanager.ConnectionManager, options ExchangeOptions) error { | |||||
| if !options.Declare { | |||||
| return nil | |||||
| } | } | ||||
| return options.Exchange | |||||
| } | |||||
| // getQueueOptionsOrSetDefault returns pointer to current QueueOptions options. | |||||
| // If no queue options are set yet, new options with default values will be defined. | |||||
| func getQueueOptionsOrSetDefault(options *DeclareOptions) *QueueOptions { | |||||
| if options.Queue == nil { | |||||
| options.Queue = &QueueOptions{ | |||||
| Name: "", | |||||
| Durable: false, | |||||
| AutoDelete: false, | |||||
| Exclusive: false, | |||||
| NoWait: false, | |||||
| Passive: false, | |||||
| Args: nil, | |||||
| if options.Passive { | |||||
| err := connManager.ExchangeDeclarePassiveSafe( | |||||
| options.Name, | |||||
| options.Kind, | |||||
| options.Durable, | |||||
| options.AutoDelete, | |||||
| options.Internal, | |||||
| options.NoWait, | |||||
| tableToAMQPTable(options.Args), | |||||
| ) | |||||
| if err != nil { | |||||
| return err | |||||
| } | } | ||||
| return nil | |||||
| } | |||||
| err := connManager.ExchangeDeclareSafe( | |||||
| options.Name, | |||||
| options.Kind, | |||||
| options.Durable, | |||||
| options.AutoDelete, | |||||
| options.Internal, | |||||
| options.NoWait, | |||||
| tableToAMQPTable(options.Args), | |||||
| ) | |||||
| if err != nil { | |||||
| return err | |||||
| } | } | ||||
| return options.Queue | |||||
| } | |||||
| // region general-options | |||||
| // WithDeclareQueue sets the queue that should be declared prior to other RabbitMQ actions are being executed. | |||||
| // Only the settings will be validated if the queue already exists on the server. | |||||
| // Matching settings will result in no action, different settings will result in an error. | |||||
| // If the 'Passive' property is set to false, a missing queue will be created on the server. | |||||
| func WithDeclareQueue(settings *QueueOptions) func(*DeclareOptions) { | |||||
| return func(options *DeclareOptions) { | |||||
| options.Queue = settings | |||||
| } | |||||
| } | |||||
| // WithDeclareExchange sets the exchange that should be declared prior to other RabbitMQ actions are being executed. | |||||
| // Only the settings will be validated if the exchange already exists on the server. | |||||
| // Matching settings will result in no action, different settings will result in an error. | |||||
| // If the 'Passive' property is set to false, a missing exchange will be created on the server. | |||||
| func WithDeclareExchange(settings *ExchangeOptions) func(*DeclareOptions) { | |||||
| return func(options *DeclareOptions) { | |||||
| options.Exchange = settings | |||||
| } | |||||
| } | |||||
| // WithDeclareBindings sets the bindings that should be declared prior to other RabbitMQ actions are being executed. | |||||
| // Only the settings will be validated if one of the bindings already exists on the server. | |||||
| // Matching settings will result in no action, different settings will result in an error. | |||||
| // If the 'Passive' property is set to false, missing bindings will be created on the server. | |||||
| func WithDeclareBindings(bindings []Binding) func(*DeclareOptions) { | |||||
| return func(options *DeclareOptions) { | |||||
| options.Bindings = bindings | |||||
| } | |||||
| } | |||||
| // WithDeclareBindingsForRoutingKeys sets the bindings that should be declared prior to other RabbitMQ | |||||
| // actions are being executed. | |||||
| // This function must be called after the queue and exchange declaration settings have been set, | |||||
| // otherwise this function has no effect. | |||||
| func WithDeclareBindingsForRoutingKeys(routingKeys []string) func(*DeclareOptions) { | |||||
| return func(options *DeclareOptions) { | |||||
| options.SetBindings(routingKeys, BindingOption{}) | |||||
| } | |||||
| } | |||||
| // endregion general-options | |||||
| // region single-options | |||||
| // WithDeclareQueueName returns a function that sets the queue name. | |||||
| func WithDeclareQueueName(name string) func(*DeclareOptions) { | |||||
| return func(options *DeclareOptions) { | |||||
| getQueueOptionsOrSetDefault(options).Name = name | |||||
| } | |||||
| } | |||||
| // WithDeclareQueueDurable sets the queue to durable, which means it won't | |||||
| // be destroyed when the server restarts. It must only be bound to durable exchanges. | |||||
| func WithDeclareQueueDurable(options *DeclareOptions) { | |||||
| getQueueOptionsOrSetDefault(options).Durable = true | |||||
| } | |||||
| // WithDeclareQueueAutoDelete sets the queue to auto delete, which means it will | |||||
| // be deleted when there are no more consumers on it. | |||||
| func WithDeclareQueueAutoDelete(options *DeclareOptions) { | |||||
| getQueueOptionsOrSetDefault(options).AutoDelete = true | |||||
| } | |||||
| // WithDeclareQueueExclusive sets the queue to exclusive, which means | |||||
| // it's are only accessible by the connection that declares it and | |||||
| // will be deleted when the connection closes. Channels on other connections | |||||
| // will receive an error when attempting to declare, bind, consume, purge or | |||||
| // delete a queue with the same name. | |||||
| func WithDeclareQueueExclusive(options *DeclareOptions) { | |||||
| getQueueOptionsOrSetDefault(options).Exclusive = true | |||||
| } | |||||
| // WithDeclareQueueNoWait sets the queue to nowait, which means | |||||
| // the queue will assume to be declared on the server. A channel | |||||
| // exception will arrive if the conditions are met for existing queues | |||||
| // or attempting to modify an existing queue from a different connection. | |||||
| func WithDeclareQueueNoWait(options *DeclareOptions) { | |||||
| getQueueOptionsOrSetDefault(options).NoWait = true | |||||
| } | |||||
| // WithDeclareQueueNoDeclare sets the queue to no declare, which means | |||||
| // the queue will be assumed to be declared on the server, and thus only will be validated. | |||||
| func WithDeclareQueueNoDeclare(options *DeclareOptions) { | |||||
| getQueueOptionsOrSetDefault(options).Passive = true | |||||
| } | |||||
| // WithDeclareQueueArgs returns a function that sets the queue arguments. | |||||
| func WithDeclareQueueArgs(args Table) func(*DeclareOptions) { | |||||
| return func(options *DeclareOptions) { | |||||
| getQueueOptionsOrSetDefault(options).Args = args | |||||
| } | |||||
| } | |||||
| // WithDeclareQueueQuorum sets the queue a quorum type, which means multiple nodes | |||||
| // in the cluster will have the messages distributed amongst them for higher reliability. | |||||
| func WithDeclareQueueQuorum(options *DeclareOptions) { | |||||
| queue := getQueueOptionsOrSetDefault(options) | |||||
| if queue.Args == nil { | |||||
| queue.Args = Table{} | |||||
| } | |||||
| queue.Args["x-queue-type"] = "quorum" | |||||
| } | |||||
| // WithDeclareExchangeName returns a function that sets the exchange name. | |||||
| func WithDeclareExchangeName(name string) func(*DeclareOptions) { | |||||
| return func(options *DeclareOptions) { | |||||
| getExchangeOptionsOrSetDefault(options).Name = name | |||||
| } | |||||
| } | |||||
| // WithDeclareExchangeKind returns a function that sets the binding exchange kind/type. | |||||
| func WithDeclareExchangeKind(kind string) func(*DeclareOptions) { | |||||
| return func(options *DeclareOptions) { | |||||
| getExchangeOptionsOrSetDefault(options).Kind = kind | |||||
| } | |||||
| } | |||||
| // WithDeclareExchangeDurable returns a function that sets the binding exchange durable flag. | |||||
| func WithDeclareExchangeDurable(options *DeclareOptions) { | |||||
| getExchangeOptionsOrSetDefault(options).Durable = true | |||||
| } | |||||
| // WithDeclareExchangeAutoDelete returns a function that sets the binding exchange autoDelete flag. | |||||
| func WithDeclareExchangeAutoDelete(options *DeclareOptions) { | |||||
| getExchangeOptionsOrSetDefault(options).AutoDelete = true | |||||
| } | |||||
| // WithDeclareExchangeInternal returns a function that sets the binding exchange internal flag. | |||||
| func WithDeclareExchangeInternal(options *DeclareOptions) { | |||||
| getExchangeOptionsOrSetDefault(options).Internal = true | |||||
| } | |||||
| // WithDeclareExchangeNoWait returns a function that sets the binding exchange noWait flag. | |||||
| func WithDeclareExchangeNoWait(options *DeclareOptions) { | |||||
| getExchangeOptionsOrSetDefault(options).NoWait = true | |||||
| } | |||||
| // WithDeclareExchangeArgs returns a function that sets the binding exchange arguments | |||||
| // that are specific to the server's implementation of the exchange. | |||||
| func WithDeclareExchangeArgs(args Table) func(*DeclareOptions) { | |||||
| return func(options *DeclareOptions) { | |||||
| getExchangeOptionsOrSetDefault(options).Args = args | |||||
| } | |||||
| } | |||||
| // WithDeclareExchangeNoDeclare returns a function that skips the declaration of the | |||||
| // binding exchange. Use this setting if the exchange already exists and you don't need to declare | |||||
| // it on consumer start. | |||||
| func WithDeclareExchangeNoDeclare(options *DeclareOptions) { | |||||
| getExchangeOptionsOrSetDefault(options).Passive = true | |||||
| } | |||||
| // WithDeclareBindingNoWait sets the bindings to nowait, which means if the queue can not be bound | |||||
| // the channel will not be closed with an error. | |||||
| // This function must be called after bindings have been defined, otherwise it has no effect. | |||||
| func WithDeclareBindingNoWait(options *DeclareOptions) { | |||||
| for i := range options.Bindings { | |||||
| options.Bindings[i].NoWait = true | |||||
| } | |||||
| return nil | |||||
| } | } | ||||
| // WithDeclareBindingArgs sets the arguments of the bindings to args. | |||||
| // This function must be called after bindings have been defined, otherwise it has no effect. | |||||
| func WithDeclareBindingArgs(args Table) func(*DeclareOptions) { | |||||
| return func(options *DeclareOptions) { | |||||
| for i := range options.Bindings { | |||||
| options.Bindings[i].Args = args | |||||
| func declareBindings(connManager *connectionmanager.ConnectionManager, options ConsumeOptions) error { | |||||
| for _, binding := range options.Bindings { | |||||
| if !binding.Declare { | |||||
| continue | |||||
| } | |||||
| err := connManager.QueueBindSafe( | |||||
| options.QueueOptions.Name, | |||||
| binding.RoutingKey, | |||||
| options.ExchangeOptions.Name, | |||||
| binding.NoWait, | |||||
| tableToAMQPTable(binding.Args), | |||||
| ) | |||||
| if err != nil { | |||||
| return err | |||||
| } | } | ||||
| } | } | ||||
| return nil | |||||
| } | } | ||||
| // endregion single-options | |||||
| @ -1 +0,0 @@ | |||||
| consumer_with_declare | |||||
| @ -1,74 +0,0 @@ | |||||
| package main | |||||
| import ( | |||||
| "fmt" | |||||
| "log" | |||||
| "os" | |||||
| "os/signal" | |||||
| "syscall" | |||||
| rabbitmq "github.com/wagslane/go-rabbitmq" | |||||
| ) | |||||
| var consumerName = "example_with_declare" | |||||
| func main() { | |||||
| consumer, err := rabbitmq.NewConsumer( | |||||
| "amqp://guest:guest@localhost", rabbitmq.Config{}, | |||||
| rabbitmq.WithConsumerOptionsLogging, | |||||
| ) | |||||
| if err != nil { | |||||
| log.Fatal(err) | |||||
| } | |||||
| defer func() { | |||||
| err := consumer.Close() | |||||
| if err != nil { | |||||
| log.Fatal(err) | |||||
| } | |||||
| }() | |||||
| err = consumer.StartConsuming( | |||||
| func(d rabbitmq.Delivery) rabbitmq.Action { | |||||
| log.Printf("consumed: %v", string(d.Body)) | |||||
| // rabbitmq.Ack, rabbitmq.NackDiscard, rabbitmq.NackRequeue | |||||
| return rabbitmq.Ack | |||||
| }, | |||||
| "my_queue", | |||||
| rabbitmq.WithConsumeDeclareOptions( | |||||
| rabbitmq.WithDeclareQueueDurable, | |||||
| rabbitmq.WithDeclareQueueQuorum, | |||||
| rabbitmq.WithDeclareExchangeName("events"), | |||||
| rabbitmq.WithDeclareExchangeKind("topic"), | |||||
| rabbitmq.WithDeclareExchangeDurable, | |||||
| rabbitmq.WithDeclareBindingsForRoutingKeys([]string{"routing_key", "routing_key_2"}), // implicit bindings | |||||
| rabbitmq.WithDeclareBindings([]rabbitmq.Binding{ // custom bindings | |||||
| { | |||||
| QueueName: "my_queue", | |||||
| ExchangeName: "events", | |||||
| RoutingKey: "a_custom_key", | |||||
| }, | |||||
| }), | |||||
| ), | |||||
| rabbitmq.WithConsumeOptionsConsumerName(consumerName), | |||||
| ) | |||||
| if err != nil { | |||||
| log.Fatal(err) | |||||
| } | |||||
| // block main thread - wait for shutdown signal | |||||
| sigs := make(chan os.Signal, 1) | |||||
| done := make(chan bool, 1) | |||||
| signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) | |||||
| go func() { | |||||
| sig := <-sigs | |||||
| fmt.Println() | |||||
| fmt.Println(sig) | |||||
| done <- true | |||||
| }() | |||||
| fmt.Println("awaiting signal") | |||||
| <-done | |||||
| fmt.Println("stopping consumer") | |||||
| } | |||||
| @ -0,0 +1,16 @@ | |||||
| package rabbitmq | |||||
| // ExchangeOptions are used to configure an exchange. | |||||
| // If the Passive flag is set the client will only check if the exchange exists on the server | |||||
| // and that the settings match, no creation attempt will be made. | |||||
| type ExchangeOptions struct { | |||||
| Name string | |||||
| Kind string // possible values: empty string for default exchange or direct, topic, fanout | |||||
| Durable bool | |||||
| AutoDelete bool | |||||
| Internal bool | |||||
| NoWait bool | |||||
| Passive bool // if false, a missing exchange will be created on the server | |||||
| Args Table | |||||
| Declare bool | |||||
| } | |||||
| @ -0,0 +1,160 @@ | |||||
| package connectionmanager | |||||
| import ( | |||||
| "errors" | |||||
| "sync" | |||||
| "time" | |||||
| amqp "github.com/rabbitmq/amqp091-go" | |||||
| "github.com/wagslane/go-rabbitmq/internal/logger" | |||||
| ) | |||||
| // ConnectionManager - | |||||
| type ConnectionManager struct { | |||||
| logger logger.Logger | |||||
| url string | |||||
| channel *amqp.Channel | |||||
| connection *amqp.Connection | |||||
| amqpConfig amqp.Config | |||||
| channelMux *sync.RWMutex | |||||
| reconnectInterval time.Duration | |||||
| reconnectionCount uint | |||||
| reconnectionCountMux *sync.Mutex | |||||
| dispatcher *dispatcher | |||||
| } | |||||
| // NewConnectionManager creates a new connection manager | |||||
| func NewConnectionManager(url string, conf amqp.Config, log logger.Logger, reconnectInterval time.Duration) (*ConnectionManager, error) { | |||||
| conn, ch, err := getNewChannel(url, conf) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| connManager := ConnectionManager{ | |||||
| logger: log, | |||||
| url: url, | |||||
| connection: conn, | |||||
| channel: ch, | |||||
| channelMux: &sync.RWMutex{}, | |||||
| amqpConfig: conf, | |||||
| reconnectInterval: reconnectInterval, | |||||
| reconnectionCount: 0, | |||||
| reconnectionCountMux: &sync.Mutex{}, | |||||
| dispatcher: newDispatcher(), | |||||
| } | |||||
| go connManager.startNotifyCancelOrClosed() | |||||
| return &connManager, nil | |||||
| } | |||||
| func getNewChannel(url string, conf amqp.Config) (*amqp.Connection, *amqp.Channel, error) { | |||||
| amqpConn, err := amqp.DialConfig(url, amqp.Config(conf)) | |||||
| if err != nil { | |||||
| return nil, nil, err | |||||
| } | |||||
| ch, err := amqpConn.Channel() | |||||
| if err != nil { | |||||
| return nil, nil, err | |||||
| } | |||||
| return amqpConn, ch, nil | |||||
| } | |||||
| // startNotifyCancelOrClosed listens on the channel's cancelled and closed | |||||
| // notifiers. When it detects a problem, it attempts to reconnect. | |||||
| // Once reconnected, it sends an error back on the manager's notifyCancelOrClose | |||||
| // channel | |||||
| func (connManager *ConnectionManager) startNotifyCancelOrClosed() { | |||||
| notifyCloseChan := connManager.channel.NotifyClose(make(chan *amqp.Error, 1)) | |||||
| notifyCancelChan := connManager.channel.NotifyCancel(make(chan string, 1)) | |||||
| select { | |||||
| case err := <-notifyCloseChan: | |||||
| if err != nil { | |||||
| connManager.logger.Errorf("attempting to reconnect to amqp server after close with error: %v", err) | |||||
| connManager.reconnectLoop() | |||||
| connManager.logger.Warnf("successfully reconnected to amqp server") | |||||
| connManager.dispatcher.dispatch(err) | |||||
| } | |||||
| if err == nil { | |||||
| connManager.logger.Infof("amqp channel closed gracefully") | |||||
| } | |||||
| case err := <-notifyCancelChan: | |||||
| connManager.logger.Errorf("attempting to reconnect to amqp server after cancel with error: %s", err) | |||||
| connManager.reconnectLoop() | |||||
| connManager.logger.Warnf("successfully reconnected to amqp server after cancel") | |||||
| connManager.dispatcher.dispatch(errors.New(err)) | |||||
| } | |||||
| } | |||||
| // GetReconnectionCount - | |||||
| func (connManager *ConnectionManager) GetReconnectionCount() uint { | |||||
| connManager.reconnectionCountMux.Lock() | |||||
| defer connManager.reconnectionCountMux.Unlock() | |||||
| return connManager.reconnectionCount | |||||
| } | |||||
| func (connManager *ConnectionManager) incrementReconnectionCount() { | |||||
| connManager.reconnectionCountMux.Lock() | |||||
| defer connManager.reconnectionCountMux.Unlock() | |||||
| connManager.reconnectionCount++ | |||||
| } | |||||
| // reconnectLoop continuously attempts to reconnect | |||||
| func (connManager *ConnectionManager) reconnectLoop() { | |||||
| for { | |||||
| connManager.logger.Infof("waiting %s seconds to attempt to reconnect to amqp server", connManager.reconnectInterval) | |||||
| time.Sleep(connManager.reconnectInterval) | |||||
| err := connManager.reconnect() | |||||
| if err != nil { | |||||
| connManager.logger.Errorf("error reconnecting to amqp server: %v", err) | |||||
| } else { | |||||
| connManager.incrementReconnectionCount() | |||||
| go connManager.startNotifyCancelOrClosed() | |||||
| return | |||||
| } | |||||
| } | |||||
| } | |||||
| // reconnect safely closes the current channel and obtains a new one | |||||
| func (connManager *ConnectionManager) reconnect() error { | |||||
| connManager.channelMux.Lock() | |||||
| defer connManager.channelMux.Unlock() | |||||
| newConn, newChannel, err := getNewChannel(connManager.url, connManager.amqpConfig) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| if err = connManager.channel.Close(); err != nil { | |||||
| connManager.logger.Warnf("error closing channel while reconnecting: %v", err) | |||||
| } | |||||
| if err = connManager.connection.Close(); err != nil { | |||||
| connManager.logger.Warnf("error closing connection while reconnecting: %v", err) | |||||
| } | |||||
| connManager.connection = newConn | |||||
| connManager.channel = newChannel | |||||
| return nil | |||||
| } | |||||
| // close safely closes the current channel and connection | |||||
| func (connManager *ConnectionManager) close() error { | |||||
| connManager.logger.Infof("closing connection manager...") | |||||
| connManager.channelMux.Lock() | |||||
| defer connManager.channelMux.Unlock() | |||||
| err := connManager.channel.Close() | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| err = connManager.connection.Close() | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // NotifyReconnect adds a new subscriber that will receive error messages whenever | |||||
| // the connection manager has successfully reconnect to the server | |||||
| func (connManager *ConnectionManager) NotifyReconnect() (<-chan error, chan<- struct{}) { | |||||
| return connManager.dispatcher.addSubscriber() | |||||
| } | |||||
| @ -0,0 +1,68 @@ | |||||
| package connectionmanager | |||||
| import ( | |||||
| "log" | |||||
| "math" | |||||
| "math/rand" | |||||
| "sync" | |||||
| "time" | |||||
| ) | |||||
| type dispatcher struct { | |||||
| subscribers map[int]dispatchSubscriber | |||||
| subscribersMux *sync.Mutex | |||||
| } | |||||
| type dispatchSubscriber struct { | |||||
| notifyCancelOrCloseChan chan error | |||||
| closeCh <-chan struct{} | |||||
| } | |||||
| func newDispatcher() *dispatcher { | |||||
| return &dispatcher{ | |||||
| subscribers: make(map[int]dispatchSubscriber), | |||||
| subscribersMux: &sync.Mutex{}, | |||||
| } | |||||
| } | |||||
| func (d *dispatcher) dispatch(err error) error { | |||||
| d.subscribersMux.Lock() | |||||
| defer d.subscribersMux.Unlock() | |||||
| for _, subscriber := range d.subscribers { | |||||
| select { | |||||
| case <-time.After(time.Second * 5): | |||||
| log.Println("Unexpected rabbitmq error: timeout in dispatch") | |||||
| case subscriber.notifyCancelOrCloseChan <- err: | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (d *dispatcher) addSubscriber() (<-chan error, chan<- struct{}) { | |||||
| const maxRand = math.MaxInt64 | |||||
| const minRand = 0 | |||||
| id := rand.Intn(maxRand-minRand) + minRand | |||||
| closeCh := make(chan struct{}) | |||||
| notifyCancelOrCloseChan := make(chan error) | |||||
| d.subscribersMux.Lock() | |||||
| d.subscribers[id] = dispatchSubscriber{ | |||||
| notifyCancelOrCloseChan: notifyCancelOrCloseChan, | |||||
| closeCh: closeCh, | |||||
| } | |||||
| d.subscribersMux.Unlock() | |||||
| go func(id int) { | |||||
| <-closeCh | |||||
| d.subscribersMux.Lock() | |||||
| defer d.subscribersMux.Unlock() | |||||
| sub, ok := d.subscribers[id] | |||||
| if !ok { | |||||
| return | |||||
| } | |||||
| close(sub.notifyCancelOrCloseChan) | |||||
| delete(d.subscribers, id) | |||||
| }(id) | |||||
| return notifyCancelOrCloseChan, closeCh | |||||
| } | |||||
| @ -0,0 +1,210 @@ | |||||
| package connectionmanager | |||||
| import ( | |||||
| amqp "github.com/rabbitmq/amqp091-go" | |||||
| ) | |||||
| // ConsumeSafe safely wraps the (*amqp.Channel).Consume method | |||||
| func (connManager *ConnectionManager) ConsumeSafe( | |||||
| queue, | |||||
| consumer string, | |||||
| autoAck, | |||||
| exclusive, | |||||
| noLocal, | |||||
| noWait bool, | |||||
| args amqp.Table, | |||||
| ) (<-chan amqp.Delivery, error) { | |||||
| connManager.channelMux.RLock() | |||||
| defer connManager.channelMux.RUnlock() | |||||
| return connManager.channel.Consume( | |||||
| queue, | |||||
| consumer, | |||||
| autoAck, | |||||
| exclusive, | |||||
| noLocal, | |||||
| noWait, | |||||
| args, | |||||
| ) | |||||
| } | |||||
| // QueueDeclarePassiveSafe safely wraps the (*amqp.Channel).QueueDeclarePassive method | |||||
| func (connManager *ConnectionManager) QueueDeclarePassiveSafe( | |||||
| name string, | |||||
| durable bool, | |||||
| autoDelete bool, | |||||
| exclusive bool, | |||||
| noWait bool, | |||||
| args amqp.Table, | |||||
| ) (amqp.Queue, error) { | |||||
| connManager.channelMux.RLock() | |||||
| defer connManager.channelMux.RUnlock() | |||||
| return connManager.channel.QueueDeclarePassive( | |||||
| name, | |||||
| durable, | |||||
| autoDelete, | |||||
| exclusive, | |||||
| noWait, | |||||
| args, | |||||
| ) | |||||
| } | |||||
| // QueueDeclareSafe safely wraps the (*amqp.Channel).QueueDeclare method | |||||
| func (connManager *ConnectionManager) QueueDeclareSafe( | |||||
| name string, durable bool, autoDelete bool, exclusive bool, noWait bool, args amqp.Table, | |||||
| ) (amqp.Queue, error) { | |||||
| connManager.channelMux.RLock() | |||||
| defer connManager.channelMux.RUnlock() | |||||
| return connManager.channel.QueueDeclare( | |||||
| name, | |||||
| durable, | |||||
| autoDelete, | |||||
| exclusive, | |||||
| noWait, | |||||
| args, | |||||
| ) | |||||
| } | |||||
| // ExchangeDeclarePassiveSafe safely wraps the (*amqp.Channel).ExchangeDeclarePassive method | |||||
| func (connManager *ConnectionManager) ExchangeDeclarePassiveSafe( | |||||
| name string, kind string, durable bool, autoDelete bool, internal bool, noWait bool, args amqp.Table, | |||||
| ) error { | |||||
| connManager.channelMux.RLock() | |||||
| defer connManager.channelMux.RUnlock() | |||||
| return connManager.channel.ExchangeDeclarePassive( | |||||
| name, | |||||
| kind, | |||||
| durable, | |||||
| autoDelete, | |||||
| internal, | |||||
| noWait, | |||||
| args, | |||||
| ) | |||||
| } | |||||
| // ExchangeDeclareSafe safely wraps the (*amqp.Channel).ExchangeDeclare method | |||||
| func (connManager *ConnectionManager) ExchangeDeclareSafe( | |||||
| name string, kind string, durable bool, autoDelete bool, internal bool, noWait bool, args amqp.Table, | |||||
| ) error { | |||||
| connManager.channelMux.RLock() | |||||
| defer connManager.channelMux.RUnlock() | |||||
| return connManager.channel.ExchangeDeclare( | |||||
| name, | |||||
| kind, | |||||
| durable, | |||||
| autoDelete, | |||||
| internal, | |||||
| noWait, | |||||
| args, | |||||
| ) | |||||
| } | |||||
| // QueueBindSafe safely wraps the (*amqp.Channel).QueueBind method | |||||
| func (connManager *ConnectionManager) QueueBindSafe( | |||||
| name string, key string, exchange string, noWait bool, args amqp.Table, | |||||
| ) error { | |||||
| connManager.channelMux.RLock() | |||||
| defer connManager.channelMux.RUnlock() | |||||
| return connManager.channel.QueueBind( | |||||
| name, | |||||
| key, | |||||
| exchange, | |||||
| noWait, | |||||
| args, | |||||
| ) | |||||
| } | |||||
| // QosSafe safely wraps the (*amqp.Channel).Qos method | |||||
| func (connManager *ConnectionManager) QosSafe( | |||||
| prefetchCount int, prefetchSize int, global bool, | |||||
| ) error { | |||||
| connManager.channelMux.RLock() | |||||
| defer connManager.channelMux.RUnlock() | |||||
| return connManager.channel.Qos( | |||||
| prefetchCount, | |||||
| prefetchSize, | |||||
| global, | |||||
| ) | |||||
| } | |||||
| // PublishSafe safely wraps the (*amqp.Channel).Publish method | |||||
| func (connManager *ConnectionManager) PublishSafe( | |||||
| exchange string, key string, mandatory bool, immediate bool, msg amqp.Publishing, | |||||
| ) error { | |||||
| connManager.channelMux.RLock() | |||||
| defer connManager.channelMux.RUnlock() | |||||
| return connManager.channel.Publish( | |||||
| exchange, | |||||
| key, | |||||
| mandatory, | |||||
| immediate, | |||||
| msg, | |||||
| ) | |||||
| } | |||||
| // NotifyReturnSafe safely wraps the (*amqp.Channel).NotifyReturn method | |||||
| func (connManager *ConnectionManager) NotifyReturnSafe( | |||||
| c chan amqp.Return, | |||||
| ) chan amqp.Return { | |||||
| connManager.channelMux.RLock() | |||||
| defer connManager.channelMux.RUnlock() | |||||
| return connManager.channel.NotifyReturn( | |||||
| c, | |||||
| ) | |||||
| } | |||||
| // ConfirmSafe safely wraps the (*amqp.Channel).Confirm method | |||||
| func (connManager *ConnectionManager) ConfirmSafe( | |||||
| noWait bool, | |||||
| ) error { | |||||
| connManager.channelMux.RLock() | |||||
| defer connManager.channelMux.RUnlock() | |||||
| return connManager.channel.Confirm( | |||||
| noWait, | |||||
| ) | |||||
| } | |||||
| // NotifyPublishSafe safely wraps the (*amqp.Channel).NotifyPublish method | |||||
| func (connManager *ConnectionManager) NotifyPublishSafe( | |||||
| confirm chan amqp.Confirmation, | |||||
| ) chan amqp.Confirmation { | |||||
| connManager.channelMux.RLock() | |||||
| defer connManager.channelMux.RUnlock() | |||||
| return connManager.channel.NotifyPublish( | |||||
| confirm, | |||||
| ) | |||||
| } | |||||
| // NotifyFlowSafe safely wraps the (*amqp.Channel).NotifyFlow method | |||||
| func (connManager *ConnectionManager) NotifyFlowSafe( | |||||
| c chan bool, | |||||
| ) chan bool { | |||||
| connManager.channelMux.RLock() | |||||
| defer connManager.channelMux.RUnlock() | |||||
| return connManager.channel.NotifyFlow( | |||||
| c, | |||||
| ) | |||||
| } | |||||
| // NotifyBlockedSafe safely wraps the (*amqp.Connection).NotifyBlocked method | |||||
| func (connManager *ConnectionManager) NotifyBlockedSafe( | |||||
| receiver chan amqp.Blocking, | |||||
| ) chan amqp.Blocking { | |||||
| connManager.channelMux.RLock() | |||||
| defer connManager.channelMux.RUnlock() | |||||
| return connManager.connection.NotifyBlocked( | |||||
| receiver, | |||||
| ) | |||||
| } | |||||
| @ -0,0 +1,12 @@ | |||||
| package logger | |||||
| // Logger is describes a logging structure. It can be set using | |||||
| // WithPublisherOptionsLogger() or WithConsumerOptionsLogger(). | |||||
| type Logger interface { | |||||
| Fatalf(string, ...interface{}) | |||||
| Errorf(string, ...interface{}) | |||||
| Warnf(string, ...interface{}) | |||||
| Infof(string, ...interface{}) | |||||
| Debugf(string, ...interface{}) | |||||
| Tracef(string, ...interface{}) | |||||
| } | |||||