initial commit
This commit is contained in:
227
pkg/rabbit/client.go
Normal file
227
pkg/rabbit/client.go
Normal file
@@ -0,0 +1,227 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user