Documentation
¶
Overview ¶
Package mq provides a broker-agnostic message queue abstraction with pluggable backends (RabbitMQ, Redis Streams, Kafka, NATS, …).
Architecture ¶
- Message: a unit of data with headers, payload, and metadata.
- Delivery: a Message received by a consumer, with ACK/Nack/Reject semantics.
- Producer: publishes messages to an exchange/topic.
- Consumer: subscribes to a queue/topic and dispatches deliveries to a Handler.
- Broker: manages connections, declares topology, and creates producers and consumers.
- Handler: processes a Delivery.
- Middleware:wraps Handlers for retry, logging, metrics, dead-letter, etc.
Backends ¶
- rabbitmq/ — RabbitMQ via amqp091-go (exchange/queue/binding, QoS, publisher confirms, auto-reconnect)
Basic usage ¶
broker, _ := rabbitmq.New(rabbitmq.Config{URL: "amqp://guest:guest@localhost:5672/"})
defer broker.Close()
producer, _ := broker.Producer("events", mq.PublishOptions{})
_ = producer.Publish(ctx, &mq.Message{
Body: []byte(`{"event":"user.created"}`),
})
consumer, _ := broker.Consumer("events.queue", mq.ConsumeOptions{
Handler: func(ctx context.Context, d mq.Delivery) error {
fmt.Println(string(d.Body))
return d.Ack()
},
})
_ = consumer.Start(ctx)
Index ¶
- Variables
- func DecodeJSON(d Delivery, target any) error
- func DecodeJSONBody(body []byte, target any) error
- func IsSupported(t BrokerType) bool
- func PublishBatch(ctx context.Context, p Producer, msgs []*Message) error
- type BackendInfo
- type BatchPublisher
- type Broker
- type BrokerConfig
- type BrokerType
- type ConsumeOptions
- type Consumer
- type Delivery
- type DeliveryMode
- type ExchangeOptions
- type Handler
- type HealthChecker
- type HealthStatus
- type Message
- type Metrics
- type MetricsCollector
- func (m *MetricsCollector) RecordAck()
- func (m *MetricsCollector) RecordConsume()
- func (m *MetricsCollector) RecordError()
- func (m *MetricsCollector) RecordNack()
- func (m *MetricsCollector) RecordPublish()
- func (m *MetricsCollector) RecordRedelivered()
- func (m *MetricsCollector) RecordReject()
- func (m *MetricsCollector) Snapshot() Metrics
- type Middleware
- func Chain(mw ...Middleware) Middleware
- func DeadLetterMiddleware(dlqHandler Handler) Middleware
- func LoggingMiddleware(log *slog.Logger) Middleware
- func MetricsMiddleware(collector *MetricsCollector) Middleware
- func RecoverMiddleware(log *slog.Logger) Middleware
- func RetryMiddleware(cfg RetryConfig) Middleware
- type Producer
- type PublishOptions
- type QueueOptions
- func DefaultQueueOptions() QueueOptions
- func WithDeadLetter(opts QueueOptions, dlxExchange, dlxRoutingKey string) QueueOptions
- func WithMaxPriority(opts QueueOptions, maxPriority int) QueueOptions
- func WithMessageTTL(opts QueueOptions, ttl time.Duration) QueueOptions
- func WithQueueTTL(opts QueueOptions, ttl time.Duration) QueueOptions
- type RetryConfig
- type Topology
- func (t *Topology) Apply() error
- func (t *Topology) Bind(queue, exchange, routingKey string) *Topology
- func (t *Topology) Exchange(name string, opts ...ExchangeOptions) *Topology
- func (t *Topology) MustApply()
- func (t *Topology) Queue(name string, opts ...QueueOptions) *Topology
- func (t *Topology) Reset() *Topology
Constants ¶
This section is empty.
Variables ¶
var ErrAlreadyRunning = errors.New("mq: consumer already running")
ErrAlreadyRunning is returned by Consumer.Start when the consumer is already running.
var ErrClosed = errors.New("mq: broker closed")
ErrClosed is returned when an operation is attempted on a closed broker/producer/consumer.
var ErrNoHandler = errors.New("mq: no handler configured")
ErrNoHandler is returned when a consumer is started without a handler.
var ErrNotConnected = errors.New("mq: not connected")
ErrNotConnected is returned when the broker is not connected.
var SupportedBrokers = []BrokerType{ BrokerRabbitMQ, BrokerKafka, BrokerRocketMQ, BrokerActiveMQ, BrokerRedisStream, }
SupportedBrokers lists all broker types the factory can create.
Functions ¶
func DecodeJSON ¶
DecodeJSON decodes the delivery body into the target type.
func DecodeJSONBody ¶
DecodeJSONBody decodes a raw message body into the target type.
func IsSupported ¶
func IsSupported(t BrokerType) bool
IsSupported reports whether the given broker type has a registered factory function.
Types ¶
type BackendInfo ¶
type BackendInfo struct {
Type BrokerType
Name string
Persistent bool // survives broker restart
Ordered bool // preserves message ordering
Transaction bool // supports transactions
ConsumerGroup bool // supports consumer groups
PubSub bool // supports pub/sub pattern
}
BackendInfo describes a backend implementation's capabilities.
type BatchPublisher ¶
BatchPublisher publishes multiple messages in a single broker transaction or network round-trip. Not all backends support batching; use PublishBatch as a fallback that calls Publish sequentially.
type Broker ¶
type Broker interface {
// Connect establishes the connection to the broker.
Connect() error
// IsConnected reports whether the broker is currently connected.
IsConnected() bool
// Close shuts down the broker, closing all producers and consumers
// and releasing the connection.
Close() error
// Producer creates or returns a cached producer for the given
// exchange/topic.
Producer(exchange string, opts PublishOptions) (Producer, error)
// Consumer creates a consumer for the given queue with the given
// options. The consumer is not started; call Start to begin.
Consumer(queue string, opts ConsumeOptions) (Consumer, error)
// DeclareExchange declares an exchange (RabbitMQ) or topic (Kafka).
DeclareExchange(name string, opts ExchangeOptions) error
// DeclareQueue declares a queue.
DeclareQueue(name string, opts QueueOptions) error
// Bind binds a queue to an exchange with a routing key.
Bind(queue, exchange, routingKey string) error
// Unbind removes a binding.
Unbind(queue, exchange, routingKey string) error
// DeleteQueue removes a queue.
DeleteQueue(name string) error
// DeleteExchange removes an exchange.
DeleteExchange(name string) error
}
Broker manages connections and creates producers and consumers.
type BrokerConfig ¶
type BrokerConfig struct {
Type BrokerType
// RabbitMQ configuration. Required when Type == BrokerRabbitMQ.
RabbitMQConfig any // *rabbitmq.Config
// Kafka configuration. Required when Type == BrokerKafka.
KafkaConfig any // *kafka.Config
// RocketMQ configuration. Required when Type == BrokerRocketMQ.
RocketMQConfig any // *rocketmq.Config
// ActiveMQ configuration. Required when Type == BrokerActiveMQ.
ActiveMQConfig any // *activemq.Config
// RedisStream configuration. Required when Type == BrokerRedisStream.
RedisStreamConfig any // *redisstream.Config
}
BrokerConfig is a union config that carries backend-specific settings. The factory selects the appropriate field based on BrokerType.
Usage:
cfg := mq.BrokerConfig{
Type: mq.BrokerRabbitMQ,
RabbitMQ: &rabbitmq.Config{URL: "amqp://..."},
}
broker, err := factory.NewBroker(cfg)
type BrokerType ¶
type BrokerType string
BrokerType identifies a message-queue backend implementation.
const ( // BrokerRabbitMQ selects a RabbitMQ backend. BrokerRabbitMQ BrokerType = "rabbitmq" // BrokerKafka selects a Kafka backend. BrokerKafka BrokerType = "kafka" // BrokerRocketMQ selects a RocketMQ backend. BrokerRocketMQ BrokerType = "rocketmq" // BrokerActiveMQ selects an ActiveMQ backend (STOMP protocol). BrokerActiveMQ BrokerType = "activemq" // BrokerRedisStream selects a Redis Streams backend. BrokerRedisStream BrokerType = "redisstream" )
func (BrokerType) String ¶
func (t BrokerType) String() string
String returns the broker type as a string.
type ConsumeOptions ¶
type ConsumeOptions struct {
// Handler processes each delivery. Required.
Handler Handler
// Middleware wraps the handler. Applied in order: the first
// middleware is the outermost.
Middleware []Middleware
// AutoAck: if true, messages are auto-acknowledged on delivery
// (fire-and-forget). Default: false (manual ACK).
AutoAck bool
// QosPrefetchCount is the maximum number of unacknowledged
// deliveries. Default: 10.
QosPrefetchCount int
// QosPrefetchSize is the maximum total bytes of unacknowledged
// deliveries. Default: 0 (unlimited).
QosPrefetchSize int
// QosGlobal: if true, QoS applies to the entire channel, not just
// the consumer. Default: false.
QosGlobal bool
// Concurrency is the number of goroutines processing deliveries.
// Default: 1. Set > 1 for parallel processing.
Concurrency int
// ConsumerTag identifies this consumer in broker logs.
ConsumerTag string
// Exclusive: if true, only this consumer can access the queue.
Exclusive bool
// Args are additional broker-specific arguments (e.g. x-headers).
Args map[string]any
}
ConsumeOptions configures a consumer.
type Consumer ¶
type Consumer interface {
// Start begins consuming. The consumer runs until ctx is cancelled
// or Stop is called. Start is idempotent if already running.
Start(ctx context.Context) error
// Stop gracefully stops consuming, waiting for in-flight handlers
// to complete up to the given timeout. A zero timeout returns
// immediately after signalling.
Stop(timeout time.Duration) error
// IsRunning reports whether the consumer is actively consuming.
IsRunning() bool
}
Consumer subscribes to a queue and dispatches deliveries to a Handler.
type Delivery ¶
type Delivery interface {
// Message returns the underlying message.
Message() *Message
// Body returns the raw payload (convenience shortcut).
Body() []byte
// Headers returns the message headers.
Headers() map[string]any
// RoutingKey returns the routing key.
RoutingKey() string
// Exchange returns the source exchange.
Exchange() string
// Redelivered reports whether this message has been delivered before.
Redelivered() bool
// DeliveryTag is the broker-specific delivery tag.
DeliveryTag() uint64
// Ack acknowledges successful processing. The message is removed
// from the queue.
Ack() error
// Nack negatively acknowledges the message. If requeue is true, the
// message is requeued for redelivery; otherwise it is discarded or
// routed to a dead-letter exchange (if configured).
Nack(requeue bool) error
// Reject rejects the message. If requeue is true, the message is
// requeued. Reject is similar to Nack but typically does not support
// multiple flag.
Reject(requeue bool) error
}
Delivery is a Message received by a consumer. It provides ACK/Nack/Reject semantics for at-least-once or exactly-once processing.
type DeliveryMode ¶
type DeliveryMode int
DeliveryMode selects message persistence.
const ( // Transient means the message is not persisted to disk. Transient DeliveryMode = 1 // Persistent means the message is persisted to disk (survives broker restart). Persistent DeliveryMode = 2 )
type ExchangeOptions ¶
type ExchangeOptions struct {
// Kind is the exchange type: "direct", "topic", "fanout", "headers".
// Default: "topic".
Kind string
// Durable: if true, the exchange survives broker restarts.
// Default: true.
Durable bool
// AutoDelete: if true, the exchange is deleted when no queues are
// bound. Default: false.
AutoDelete bool
// Internal: if true, the exchange cannot be published to directly
// (RabbitMQ). Default: false.
Internal bool
// NoWait: if true, do not wait for a broker confirmation.
// Default: false.
NoWait bool
// Args are additional broker-specific arguments.
Args map[string]any
}
ExchangeOptions configures an exchange declaration.
func DefaultExchangeOptions ¶
func DefaultExchangeOptions() ExchangeOptions
DefaultExchangeOptions returns sensible defaults for an exchange.
type Handler ¶
Handler processes a Delivery. Returning a nil error implies the message was processed successfully and the broker will ACK. Returning a non-nil error triggers Nack with requeue (or dead-letter, depending on config).
If the handler calls Ack/Nack/Reject explicitly, the framework will NOT auto-ACK. Use HandlerFunc to convert a function to Handler.
type HealthChecker ¶
HealthChecker is implemented by brokers that support health checks. The Check method returns nil if the broker is healthy, or an error describing the problem.
type HealthStatus ¶
type HealthStatus struct {
Healthy bool `json:"healthy"`
Backend string `json:"backend"`
Error string `json:"error,omitempty"`
Latency time.Duration `json:"latency,omitempty"`
Connected bool `json:"connected"`
}
HealthStatus describes the health of a broker.
func CheckHealth ¶
func CheckHealth(ctx context.Context, b Broker, backend string) HealthStatus
CheckHealth performs a health check with a timeout. If the broker implements HealthChecker, its Check method is called. Otherwise, IsConnected is used as a fallback.
func (HealthStatus) String ¶
func (h HealthStatus) String() string
String returns a human-readable health status.
type Message ¶
type Message struct {
// ID is a unique identifier. If empty, the broker may generate one.
ID string
// Exchange is the target exchange (RabbitMQ) or topic (Kafka/NATS).
// If empty, the producer's default exchange is used.
Exchange string
// RoutingKey is used by RabbitMQ to route the message.
RoutingKey string
// Headers are arbitrary key-value metadata.
Headers map[string]any
// Body is the raw message payload.
Body []byte
// ContentType is the MIME type of Body (e.g. "application/json").
ContentType string
// ContentEncoding is the encoding of Body (e.g. "gzip").
ContentEncoding string
// Priority is the message priority (0-9 for RabbitMQ).
Priority uint8
// CorrelationID is used for request/reply patterns.
CorrelationID string
// ReplyTo is the reply-to queue for RPC patterns.
ReplyTo string
// Expiration is the message TTL. Zero means no expiration.
Expiration time.Duration
// Timestamp is the message creation time. If zero, time.Now() is used.
Timestamp time.Time
// Type is a message type hint (optional).
Type string
// UserID is the user ID for RabbitMQ authenticated publishing.
UserID string
// AppID is the publishing application ID.
AppID string
// DeliveryMode selects persistent vs transient. Default: Persistent.
DeliveryMode DeliveryMode
}
Message is a unit of data published to the broker.
func NewJSONMessage ¶
NewJSONMessage creates a Message with a JSON-encoded body and ContentType set to "application/json".
type Metrics ¶
type Metrics struct {
Published int64
Consumed int64
Acked int64
Nacked int64
Rejected int64
Redelivered int64
Errors int64
AvgPublishMs float64
AvgConsumeMs float64
}
Metrics is a point-in-time snapshot of broker observability data.
type MetricsCollector ¶
type MetricsCollector struct {
// contains filtered or unexported fields
}
MetricsCollector is a thread-safe metrics collector.
func NewMetricsCollector ¶
func NewMetricsCollector() *MetricsCollector
NewMetricsCollector returns a fresh collector.
func (*MetricsCollector) RecordAck ¶
func (m *MetricsCollector) RecordAck()
func (*MetricsCollector) RecordConsume ¶
func (m *MetricsCollector) RecordConsume()
func (*MetricsCollector) RecordError ¶
func (m *MetricsCollector) RecordError()
func (*MetricsCollector) RecordNack ¶
func (m *MetricsCollector) RecordNack()
func (*MetricsCollector) RecordPublish ¶
func (m *MetricsCollector) RecordPublish()
MetricsCollector methods — defined here to keep mq.go focused on the public API.
func (*MetricsCollector) RecordRedelivered ¶
func (m *MetricsCollector) RecordRedelivered()
func (*MetricsCollector) RecordReject ¶
func (m *MetricsCollector) RecordReject()
func (*MetricsCollector) Snapshot ¶
func (m *MetricsCollector) Snapshot() Metrics
Snapshot returns a point-in-time copy of the metrics.
type Middleware ¶
Middleware wraps a Handler, adding cross-cutting behavior.
func Chain ¶
func Chain(mw ...Middleware) Middleware
Chain composes multiple middleware into a single middleware. The first middleware in the slice is the outermost.
func DeadLetterMiddleware ¶
func DeadLetterMiddleware(dlqHandler Handler) Middleware
DeadLetterMiddleware routes failed deliveries to a dead-letter handler and ACKs the original message so it is not redelivered.
func LoggingMiddleware ¶
func LoggingMiddleware(log *slog.Logger) Middleware
LoggingMiddleware logs each delivery before and after processing.
func MetricsMiddleware ¶
func MetricsMiddleware(collector *MetricsCollector) Middleware
MetricsMiddleware records delivery metrics in the given collector.
func RecoverMiddleware ¶
func RecoverMiddleware(log *slog.Logger) Middleware
RecoverMiddleware recovers from panics in the handler, logs the panic, and returns an error so the message is Nacked (and potentially requeued or dead-lettered).
func RetryMiddleware ¶
func RetryMiddleware(cfg RetryConfig) Middleware
RetryMiddleware retries failed deliveries up to MaxAttempts. After exhausting retries, the original error is returned so the message can be Nacked/dead-lettered.
Note: this middleware tracks attempts in-memory. For broker-level redelivery, rely on Nack(requeue=true) instead.
type Producer ¶
type Producer interface {
// Publish sends a message. If msg.ID is empty, a UUID is generated.
// If msg.Timestamp is zero, time.Now() is used.
Publish(ctx context.Context, msg *Message) error
// Close releases producer resources. After Close, Publish returns
// ErrClosed.
Close() error
}
Producer publishes messages to the broker.
type PublishOptions ¶
type PublishOptions struct {
// Mandatory: if true, the broker returns an unroutable message
// instead of silently dropping it.
Mandatory bool
// Immediate is a RabbitMQ-specific flag (deprecated in AMQP 0-9-1).
Immediate bool
// Persistent sets the default delivery mode for messages without
// an explicit DeliveryMode.
Persistent bool
// Confirm enables publisher confirms (RabbitMQ).
Confirm bool
}
PublishOptions configures a producer.
type QueueOptions ¶
type QueueOptions struct {
// Durable: if true, the queue survives broker restarts.
// Default: true.
Durable bool
// AutoDelete: if true, the queue is deleted when the last consumer
// disconnects. Default: false.
AutoDelete bool
// Exclusive: if true, the queue is only accessible by the declaring
// connection and is deleted when the connection closes.
// Default: false.
Exclusive bool
// NoWait: if true, do not wait for a broker confirmation.
// Default: false.
NoWait bool
// Args are additional broker-specific arguments (e.g.
// "x-message-ttl", "x-dead-letter-exchange").
Args map[string]any
}
QueueOptions configures a queue declaration.
func DefaultQueueOptions ¶
func DefaultQueueOptions() QueueOptions
DefaultQueueOptions returns sensible defaults for a queue.
func WithDeadLetter ¶
func WithDeadLetter(opts QueueOptions, dlxExchange, dlxRoutingKey string) QueueOptions
WithDeadLetter configures a queue to route dead-lettered messages to the specified exchange and routing key. This is RabbitMQ-specific (x-dead-letter-exchange / x-dead-letter-routing-key arguments) but the pattern is common across brokers.
func WithMaxPriority ¶
func WithMaxPriority(opts QueueOptions, maxPriority int) QueueOptions
WithMaxPriority configures a queue to support priority messages. The maxPriority value should be between 1 and 255.
func WithMessageTTL ¶
func WithMessageTTL(opts QueueOptions, ttl time.Duration) QueueOptions
WithMessageTTL configures a queue with a message time-to-live. Messages that expire are dead-lettered (if DLX is configured) or discarded.
func WithQueueTTL ¶
func WithQueueTTL(opts QueueOptions, ttl time.Duration) QueueOptions
WithQueueTTL configures a queue that is automatically deleted after the specified period of inactivity (no consumers).
type RetryConfig ¶
type RetryConfig struct {
// MaxAttempts is the maximum number of delivery attempts (including
// the first). Default: 3.
MaxAttempts int
// Backoff is the delay between retries. If BackoffFn is set, it
// takes precedence. Default: 1s.
Backoff time.Duration
// BackoffFn is an optional function that computes the backoff for
// a given attempt (1-based). If set, Backoff is ignored.
BackoffFn func(attempt int) time.Duration
}
RetryConfig configures RetryMiddleware.
func DefaultRetryConfig ¶
func DefaultRetryConfig() RetryConfig
DefaultRetryConfig returns sensible retry defaults.
type Topology ¶
type Topology struct {
// contains filtered or unexported fields
}
Topology is a fluent builder for declaring exchanges, queues, and bindings on a broker. It collects declarations and applies them in order when Apply is called.
func NewTopology ¶
NewTopology creates a new Topology builder for the given broker.
func (*Topology) Apply ¶
Apply executes all collected declarations in order: exchanges first, then queues, then bindings. Returns the first error encountered.
func (*Topology) Exchange ¶
func (t *Topology) Exchange(name string, opts ...ExchangeOptions) *Topology
Exchange declares an exchange.
func (*Topology) MustApply ¶
func (t *Topology) MustApply()
MustApply is like Apply but panics on error.