turkmentv_sms_transmitter/pkg/mq/rabbitmq.go

184 lines
3.7 KiB
Go
Raw Normal View History

2024-08-29 10:14:19 +00:00
package mq
import (
"context"
2024-09-16 07:07:05 +00:00
"fmt"
2024-08-29 10:14:19 +00:00
"log"
2024-09-16 07:07:05 +00:00
"os"
2024-08-29 10:14:19 +00:00
"time"
"github.com/streadway/amqp"
)
type MessageQueue interface {
2024-09-16 07:07:05 +00:00
Consume(processMessage func(ctx context.Context, body []byte))
2024-08-29 10:14:19 +00:00
}
type rabbitMQ struct {
rabbitMQURL string
exchangeName string
queueName string
routingKey string
2024-09-16 07:07:05 +00:00
conn *amqp.Connection
channel *amqp.Channel
2024-08-29 10:14:19 +00:00
}
2024-09-16 07:07:05 +00:00
func NewRabbitMQ() (MessageQueue, error) {
rabbitMQURL := os.Getenv("RABBITMQ_URL")
routeKey := os.Getenv("ROUTE_KEY")
queueName := os.Getenv("QUEUE_NAME")
exchangeName := os.Getenv("EXCHABGE_NAME")
if rabbitMQURL == "" || queueName == "" || exchangeName == "" || routeKey == "" {
return nil, fmt.Errorf("RABBITMQ_URL, ROUTE_KEY, EXCHABGE_NAME and QUEUE_NAME environment variables must be set")
}
2024-08-29 10:14:19 +00:00
return &rabbitMQ{
rabbitMQURL: rabbitMQURL,
exchangeName: exchangeName,
queueName: queueName,
2024-09-16 07:07:05 +00:00
routingKey: routeKey,
}, nil
}
func (r *rabbitMQ) connect() error {
var err error
r.conn, err = amqp.Dial(r.rabbitMQURL)
if err != nil {
log.Printf("Failed to connect to RabbitMQ: %v", err)
return err
}
// Open a channel
r.channel, err = r.conn.Channel()
if err != nil {
r.conn.Close()
log.Printf("Failed to open a channel: %v", err)
return err
}
// Declare exchange
err = r.channel.ExchangeDeclare(
r.exchangeName,
"direct",
true,
false,
false,
false,
nil,
)
if err != nil {
r.channel.Close()
r.conn.Close()
log.Printf("Failed to declare exchange: %v", err)
return err
2024-08-29 10:14:19 +00:00
}
2024-09-16 07:07:05 +00:00
// Declare queue
_, err = r.channel.QueueDeclare(
r.queueName,
true,
false,
false,
false,
nil,
)
if err != nil {
r.channel.Close()
r.conn.Close()
log.Printf("Failed to declare a queue: %v", err)
return err
}
// Bind queue
err = r.channel.QueueBind(
r.queueName,
r.routingKey,
r.exchangeName,
false,
nil,
)
if err != nil {
r.channel.Close()
r.conn.Close()
log.Printf("Failed to bind queue: %v", err)
return err
}
return nil
2024-08-29 10:14:19 +00:00
}
2024-09-16 07:07:05 +00:00
func (r *rabbitMQ) Consume(messageHandler func(ctx context.Context, body []byte)) {
2024-08-29 10:14:19 +00:00
2024-09-16 07:07:05 +00:00
for {
// Establish initial connection and setup
if err := r.connect(); err != nil {
log.Printf("Error setting up RabbitMQ: %v. Retrying in 5 seconds...", err)
2024-08-29 10:14:19 +00:00
time.Sleep(5 * time.Second) // Wait before retrying
continue
}
2024-09-16 07:07:05 +00:00
// Consume messages
msgs, err := r.channel.Consume(
r.queueName, // queue
"", // consumer
true, // auto-ack
2024-08-29 10:14:19 +00:00
false, // exclusive
2024-09-16 07:07:05 +00:00
false, // no-local
2024-08-29 10:14:19 +00:00
false, // no-wait
2024-09-16 07:07:05 +00:00
nil, // args
2024-08-29 10:14:19 +00:00
)
if err != nil {
log.Printf("Failed to register a consumer: %v", err)
2024-09-16 07:07:05 +00:00
r.channel.Close()
r.conn.Close()
2024-08-29 10:14:19 +00:00
time.Sleep(5 * time.Second) // Wait before retrying
continue
}
2024-09-16 07:07:05 +00:00
// Start message processing
ctx, cancel := context.WithCancel(context.Background())
2024-10-08 12:09:45 +00:00
// Create a channel to signal when message processing is done
done := make(chan bool)
go func() {
defer close(done)
for {
select {
case msg, ok := <-msgs:
if !ok {
log.Println("Message channel closed, reconnecting...")
return
}
messageHandler(ctx, msg.Body)
case <-ctx.Done():
log.Println("Context canceled, shutting down...")
return
2024-09-16 07:07:05 +00:00
}
2024-08-29 10:14:19 +00:00
}
2024-10-08 12:09:45 +00:00
}()
// Wait for either the done signal or a connection error
select {
case <-done:
// Message processing stopped, attempt to reconnect
case <-r.conn.NotifyClose(make(chan *amqp.Error)):
// Connection was closed, attempt to reconnect
2024-08-29 10:14:19 +00:00
}
2024-10-08 12:09:45 +00:00
cancel() // Cancel the context to stop message processing
r.cleanup()
log.Println("Reconnecting in 5 seconds...")
time.Sleep(5 * time.Second)
2024-08-29 10:14:19 +00:00
}
}
2024-10-08 12:09:45 +00:00
func (r *rabbitMQ) cleanup() {
if r.channel != nil {
r.channel.Close()
}
if r.conn != nil {
r.conn.Close()
}
}