228 lines
5.3 KiB
Go
228 lines
5.3 KiB
Go
package rabbitmq
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
amqp "github.com/rabbitmq/amqp091-go"
|
|
"github.com/rs/zerolog"
|
|
|
|
"base/pkg/metrics"
|
|
)
|
|
|
|
type client struct {
|
|
connectionManager ConnectionManager
|
|
publisher Publisher
|
|
consumers []Consumer
|
|
consumersMutex sync.RWMutex
|
|
config *Config
|
|
logger zerolog.Logger
|
|
}
|
|
|
|
func NewClient(config *Config, logger zerolog.Logger, metric *metrics.Metrics) (Client, error) {
|
|
if config == nil {
|
|
config = DefaultConfig()
|
|
}
|
|
|
|
config.ApplyDefaults()
|
|
if err := config.Validate(); err != nil {
|
|
return nil, fmt.Errorf("invalid configuration: %w", err)
|
|
}
|
|
|
|
connMgr, err := NewConnectionManager(config, logger)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create connection manager: %w", err)
|
|
}
|
|
|
|
c := &client{
|
|
connectionManager: connMgr,
|
|
publisher: NewPublisher(connMgr, config, logger, metric),
|
|
consumers: make([]Consumer, 0),
|
|
config: config,
|
|
logger: logger,
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
func (c *client) Publisher() Publisher {
|
|
return c.publisher
|
|
}
|
|
|
|
func (c *client) RegisterConsumer(handler MessageHandler, opts *ConsumerOptions) Consumer {
|
|
newConsumer := NewConsumer(c.connectionManager, handler, opts, c.logger)
|
|
|
|
c.consumersMutex.Lock()
|
|
c.consumers = append(c.consumers, newConsumer)
|
|
c.consumersMutex.Unlock()
|
|
|
|
c.logger.Info().Msgf("registered consumer with options: %v", opts)
|
|
return newConsumer
|
|
}
|
|
|
|
func (c *client) DeclareExchange(name string, opts ExchangeOptions) error {
|
|
ch, err := c.connectionManager.GetChannel()
|
|
if err != nil {
|
|
return NewConnectionError("get channel for exchange declaration", err)
|
|
}
|
|
defer c.connectionManager.ReturnChannel(ch)
|
|
|
|
err = ch.ExchangeDeclare(
|
|
name,
|
|
opts.Type,
|
|
opts.Durable,
|
|
opts.AutoDelete,
|
|
opts.Internal,
|
|
opts.NoWait,
|
|
opts.Args,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to declare exchange '%s': %w", name, err)
|
|
}
|
|
|
|
c.logger.Info().Str("exchange", name).
|
|
Str("type", opts.Type).
|
|
Msg("Exchange declared successfully")
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *client) DeclareQueue(name string, opts QueueOptions) error {
|
|
ch, err := c.connectionManager.GetChannel()
|
|
if err != nil {
|
|
return NewConnectionError("get channel for queue declaration", err)
|
|
}
|
|
defer c.connectionManager.ReturnChannel(ch)
|
|
|
|
args := amqp.Table{}
|
|
if opts.Args != nil {
|
|
for k, v := range opts.Args {
|
|
args[k] = v
|
|
}
|
|
}
|
|
|
|
_, err = ch.QueueDeclare(
|
|
name,
|
|
opts.Durable,
|
|
opts.AutoDelete,
|
|
opts.Exclusive,
|
|
opts.NoWait,
|
|
args,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to declare queue '%s': %w", name, err)
|
|
}
|
|
|
|
c.logger.Info().Msgf("Queue declared successfully: %s", name)
|
|
return nil
|
|
}
|
|
|
|
func (c *client) BindQueue(queue, exchange, routingKey string) error {
|
|
ch, err := c.connectionManager.GetChannel()
|
|
if err != nil {
|
|
return NewConnectionError("get channel for queue binding", err)
|
|
}
|
|
defer c.connectionManager.ReturnChannel(ch)
|
|
|
|
err = ch.QueueBind(
|
|
queue,
|
|
routingKey,
|
|
exchange,
|
|
false,
|
|
nil,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to bind queue '%s' to exchange '%s' with routing key '%s': %w", queue, exchange, routingKey, err)
|
|
}
|
|
|
|
c.logger.Info().Msgf("Queue binded successfully: %s", queue)
|
|
return nil
|
|
}
|
|
|
|
func (c *client) DeleteQueue(name string) error {
|
|
ch, err := c.connectionManager.GetChannel()
|
|
if err != nil {
|
|
return NewConnectionError("get channel for queue deletion", err)
|
|
}
|
|
defer c.connectionManager.ReturnChannel(ch)
|
|
|
|
_, err = ch.QueueDelete(
|
|
name,
|
|
false, // ifUnused
|
|
false, // ifEmpty
|
|
false, // noWait
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete queue '%s': %w", name, err)
|
|
}
|
|
|
|
c.logger.Info().Msgf("Queue deleted successfully: %s", name)
|
|
return nil
|
|
}
|
|
|
|
func (c *client) DeleteExchange(name string) error {
|
|
ch, err := c.connectionManager.GetChannel()
|
|
if err != nil {
|
|
return NewConnectionError("get channel for exchange deletion", err)
|
|
}
|
|
defer c.connectionManager.ReturnChannel(ch)
|
|
|
|
err = ch.ExchangeDelete(
|
|
name,
|
|
false, // ifUnused
|
|
false, // noWait
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete exchange '%s': %w", name, err)
|
|
}
|
|
|
|
c.logger.Info().Msgf("Exchange deleted successfully: %s", name)
|
|
return nil
|
|
}
|
|
|
|
func (c *client) HealthCheck() error {
|
|
if !c.connectionManager.IsConnected() {
|
|
return ErrConnectionLost
|
|
}
|
|
|
|
// Try to get a channel and perform a basic operation
|
|
ch, err := c.connectionManager.GetChannel()
|
|
if err != nil {
|
|
return NewConnectionError("health check channel creation", err)
|
|
}
|
|
defer c.connectionManager.ReturnChannel(ch)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *client) Close() error {
|
|
c.logger.Info().Msg("Closing RabbitMQ client...")
|
|
|
|
var closeErrors []error
|
|
|
|
if err := c.publisher.Close(); err != nil {
|
|
closeErrors = append(closeErrors, fmt.Errorf("publisher close error: %w", err))
|
|
}
|
|
|
|
// Close all additional consumers
|
|
c.consumersMutex.Lock()
|
|
for i, consumer := range c.consumers {
|
|
if err := consumer.Close(); err != nil {
|
|
closeErrors = append(closeErrors, fmt.Errorf("consumer %d close error: %w", i, err))
|
|
}
|
|
}
|
|
c.consumers = nil // Clear the slice
|
|
c.consumersMutex.Unlock()
|
|
|
|
if err := c.connectionManager.Close(); err != nil {
|
|
closeErrors = append(closeErrors, fmt.Errorf("connection manager close error: %w", err))
|
|
}
|
|
|
|
if len(closeErrors) > 0 {
|
|
return fmt.Errorf("errors during close: %v", closeErrors)
|
|
}
|
|
|
|
c.logger.Info().Msg("RabbitMQ client closed successfully")
|
|
return nil
|
|
}
|