mirror of
https://github.com/getAlby/lndhub.go.git
synced 2025-12-20 06:05:08 +01:00
196 lines
4.9 KiB
Go
196 lines
4.9 KiB
Go
package rabbitmq
|
|
|
|
import (
|
|
"context"
|
|
|
|
amqp "github.com/rabbitmq/amqp091-go"
|
|
)
|
|
|
|
type AMQPClient interface {
|
|
Listen(ctx context.Context, exchange string, routingKey string, queueName string, options ...AMQPListenOptions) (<-chan amqp.Delivery, error)
|
|
PublishWithContext(ctx context.Context, exchange, key string, mandatory, immediate bool, msg amqp.Publishing) error
|
|
ExchangeDeclare(name, kind string, durable, autoDelete, internal, noWait bool, args amqp.Table) error
|
|
Close() error
|
|
}
|
|
|
|
type DefaultAMQPCLient struct {
|
|
conn *amqp.Connection
|
|
|
|
// It is recommended that, when possible, publishers and consumers
|
|
// use separate connections so that consumers are isolated from potential
|
|
// flow control measures that may be applied to publishing connections.
|
|
consumeChannel *amqp.Channel
|
|
publishChannel *amqp.Channel
|
|
}
|
|
|
|
func (c *DefaultAMQPCLient) Close() error { return c.conn.Close() }
|
|
|
|
func (c *DefaultAMQPCLient) ExchangeDeclare(name, kind string, durable, autoDelete, internal, noWait bool, args amqp.Table) error {
|
|
// TODO: Seperate management channel? Or provide way to select channel?
|
|
ch, err := c.conn.Channel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer ch.Close()
|
|
|
|
|
|
return ch.ExchangeDeclare(name, kind, durable, autoDelete, internal, noWait, args)
|
|
}
|
|
|
|
|
|
type listenOptions struct {
|
|
Durable bool
|
|
AutoDelete bool
|
|
Internal bool
|
|
Wait bool
|
|
Exclusive bool
|
|
AutoAck bool
|
|
}
|
|
|
|
type AMQPListenOptions = func(opts listenOptions) listenOptions
|
|
|
|
func WithDurable(durable bool) AMQPListenOptions {
|
|
return func(opts listenOptions) listenOptions {
|
|
opts.Durable = durable
|
|
return opts
|
|
}
|
|
}
|
|
|
|
func WithAutoDelete(autoDelete bool) AMQPListenOptions {
|
|
return func(opts listenOptions) listenOptions {
|
|
opts.AutoDelete = autoDelete
|
|
return opts
|
|
}
|
|
}
|
|
|
|
func WithInternal(internal bool) AMQPListenOptions {
|
|
return func(opts listenOptions) listenOptions {
|
|
opts.Internal = internal
|
|
return opts
|
|
}
|
|
}
|
|
|
|
func WithWait(wait bool) AMQPListenOptions {
|
|
return func(opts listenOptions) listenOptions {
|
|
opts.Wait = wait
|
|
return opts
|
|
}
|
|
}
|
|
|
|
func WithExclusive(exclusive bool) AMQPListenOptions {
|
|
return func(opts listenOptions) listenOptions {
|
|
opts.Exclusive = exclusive
|
|
return opts
|
|
}
|
|
}
|
|
|
|
func WithAutoAck(autoAck bool) AMQPListenOptions {
|
|
return func(opts listenOptions) listenOptions {
|
|
opts.AutoAck = autoAck
|
|
return opts
|
|
}
|
|
}
|
|
|
|
func (c *DefaultAMQPCLient) Listen(ctx context.Context, exchange string, routingKey string, queueName string, options ...AMQPListenOptions) (<-chan amqp.Delivery, error) {
|
|
opts := listenOptions{
|
|
Durable: true,
|
|
AutoDelete: false,
|
|
Internal: false,
|
|
Wait: false,
|
|
Exclusive: false,
|
|
AutoAck: false,
|
|
}
|
|
|
|
for _, opt := range options {
|
|
opts = opt(opts)
|
|
}
|
|
|
|
err := c.consumeChannel.ExchangeDeclare(
|
|
exchange,
|
|
// topic is a type of exchange that allows routing messages to different queue's bases on a routing key
|
|
"topic",
|
|
// Durable and Non-Auto-Deleted exchanges will survive server restarts and remain
|
|
// declared when there are no remaining bindings.
|
|
opts.Durable,
|
|
opts.AutoDelete,
|
|
// Non-Internal exchange's accept direct publishing
|
|
opts.Internal,
|
|
// Nowait: We set this to false as we want to wait for a server response
|
|
// to check whether the exchange was created succesfully
|
|
opts.Wait,
|
|
nil,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
queue, err := c.consumeChannel.QueueDeclare(
|
|
queueName,
|
|
// Durable and Non-Auto-Deleted queues will survive server restarts and remain
|
|
// declared when there are no remaining bindings.
|
|
opts.Durable,
|
|
opts.AutoDelete,
|
|
// None-Exclusive means other consumers can consume from this queue.
|
|
// Messages from queues are spread out and load balanced between consumers.
|
|
// So multiple lndhub.go instances will spread the load of invoices between them
|
|
opts.Exclusive,
|
|
// Nowait: We set this to false as we want to wait for a server response
|
|
// to check whether the queue was created successfully
|
|
opts.Wait,
|
|
nil,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = c.consumeChannel.QueueBind(
|
|
queue.Name,
|
|
routingKey,
|
|
exchange,
|
|
// Nowait: We set this to false as we want to wait for a server response
|
|
// to check whether the queue was created successfully
|
|
opts.Wait,
|
|
nil,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return c.consumeChannel.Consume(
|
|
queue.Name,
|
|
"",
|
|
opts.AutoAck,
|
|
opts.Exclusive,
|
|
false,
|
|
opts.Wait,
|
|
nil,
|
|
)
|
|
}
|
|
|
|
func (c *DefaultAMQPCLient) PublishWithContext(ctx context.Context, exchange string, key string, mandatory bool, immediate bool, msg amqp.Publishing) error {
|
|
return c.publishChannel.PublishWithContext(ctx, exchange, key, mandatory, immediate, msg)
|
|
}
|
|
|
|
func Dial(uri string) (AMQPClient, error) {
|
|
conn, err := amqp.Dial(uri)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
consumeChannel, err := conn.Channel()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
publishChannel, err := conn.Channel()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &DefaultAMQPCLient{
|
|
conn,
|
|
consumeChannel,
|
|
publishChannel,
|
|
}, nil
|
|
}
|