fixed reconnection logic

This commit is contained in:
merdan 2024-09-16 12:07:05 +05:00
parent 924963eeef
commit 90bf09c1b5
2 changed files with 123 additions and 81 deletions

View File

@ -2,15 +2,13 @@ package main
import ( import (
"context" "context"
"fmt"
"log" "log"
"os" "os"
"os/signal" "os/signal"
"syscall"
"smpp-transmitter/pkg/data" "smpp-transmitter/pkg/data"
fl "smpp-transmitter/pkg/logger" fl "smpp-transmitter/pkg/logger"
"smpp-transmitter/pkg/mq" "smpp-transmitter/pkg/mq"
"syscall"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"gorm.io/driver/mysql" "gorm.io/driver/mysql"
@ -25,41 +23,35 @@ func main() {
} }
// Load environment variables // Load environment variables
rabbitMQURL := os.Getenv("RABBITMQ_URL")
mysqlDSN := os.Getenv("MYSQL_DSN") mysqlDSN := os.Getenv("MYSQL_DSN")
queueName := os.Getenv("QUEUE_NAME")
exchangeName := os.Getenv("EXCHABGE_NAME")
appEnv := os.Getenv("APP_ENV") appEnv := os.Getenv("APP_ENV")
routeKey := os.Getenv("ROUTE_KEY")
if rabbitMQURL == "" || mysqlDSN == "" || queueName == "" || exchangeName == "" || routeKey == "" { if mysqlDSN == "" {
log.Fatal("RABBITMQ_URL, MYSQL_DSN, and QUEUE_NAME environment variables must be set") log.Fatal("MYSQL_DSN environment variable must be set")
} }
Log, err := fl.SetupLogger(appEnv) Log, err := fl.SetupLogger(appEnv)
if err != nil { if err != nil {
log.Fatalf("Failed to set up logger: %v", err) log.Fatalf("Failed to set up logger: %v", err)
} }
fmt.Printf("DB: %s", mysqlDSN)
// Initialize MySQL database connection // Initialize MySQL database connection
db, err := gorm.Open(mysql.Open(mysqlDSN), &gorm.Config{ db, err := gorm.Open(mysql.Open(mysqlDSN), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info), Logger: logger.Default.LogMode(logger.Info),
}) })
if err != nil { if err != nil {
Log.Error("Failed to connect to MySQL:", err) Log.Error("Failed to connect to MySQL:", err)
log.Fatalf("Failed to connect to MySQL: %v", err) panic("Failed to connect to MySQL")
} }
//initilize rabbitmq //initilize rabbitmq
messageQueue := mq.NewRabbitMQ(rabbitMQURL, exchangeName, queueName, routeKey) messageQueue, err := mq.NewRabbitMQ()
if err != nil {
// Start message processing log.Fatalf("Failed to set up Message Broker: %v", err)
ctx, cancel := context.WithCancel(context.Background()) }
defer cancel()
// Run RabbitMQ consumer // Run RabbitMQ consumer
go messageQueue.Consume(ctx, func(ctx context.Context, body []byte) { go messageQueue.Consume(func(ctx context.Context, body []byte) {
message := &data.Message{} message := &data.Message{}
Log.Info("Received a raw message", "body", string(body)) Log.Info("Received a raw message", "body", string(body))
@ -74,7 +66,7 @@ func main() {
return return
} }
fmt.Printf("Message handled %v", string(body)) log.Printf("Message handled %v", string(body))
}) })
@ -84,4 +76,5 @@ func main() {
<-sigs <-sigs
log.Println("Shutting down...") log.Println("Shutting down...")
Log.Info("Shutting down...") Log.Info("Shutting down...")
os.Exit(0)
} }

View File

@ -2,14 +2,16 @@ package mq
import ( import (
"context" "context"
"fmt"
"log" "log"
"os"
"time" "time"
"github.com/streadway/amqp" "github.com/streadway/amqp"
) )
type MessageQueue interface { type MessageQueue interface {
Consume(ctx context.Context, processMessage func(ctx context.Context, body []byte)) Consume(processMessage func(ctx context.Context, body []byte))
} }
type rabbitMQ struct { type rabbitMQ struct {
@ -17,74 +19,108 @@ type rabbitMQ struct {
exchangeName string exchangeName string
queueName string queueName string
routingKey string routingKey string
conn *amqp.Connection
channel *amqp.Channel
} }
func NewRabbitMQ(rabbitMQURL, exchangeName, queueName, routingKey string) MessageQueue { 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")
}
return &rabbitMQ{ return &rabbitMQ{
rabbitMQURL: rabbitMQURL, rabbitMQURL: rabbitMQURL,
exchangeName: exchangeName, exchangeName: exchangeName,
queueName: queueName, queueName: queueName,
routingKey: routingKey, routingKey: routeKey,
} }, nil
} }
func (r *rabbitMQ) Consume(ctx context.Context, messageHandler func(ctx context.Context, body []byte)) { func (r *rabbitMQ) connect() error {
for { var err error
conn, err := amqp.Dial(r.rabbitMQURL) r.conn, err = amqp.Dial(r.rabbitMQURL)
if err != nil { if err != nil {
log.Printf("Failed to connect to RabbitMQ: %v", err) log.Printf("Failed to connect to RabbitMQ: %v", err)
time.Sleep(5 * time.Second) // Wait before retrying return err
continue
} }
defer conn.Close()
ch, err := conn.Channel() // Open a channel
r.channel, err = r.conn.Channel()
if err != nil { if err != nil {
r.conn.Close()
log.Printf("Failed to open a channel: %v", err) log.Printf("Failed to open a channel: %v", err)
time.Sleep(5 * time.Second) // Wait before retrying return err
continue
} }
defer ch.Close()
// Declare the exchange // Declare exchange
err = ch.ExchangeDeclare( err = r.channel.ExchangeDeclare(
r.exchangeName, // name of the exchange r.exchangeName,
"direct", // type of exchange "direct",
true, // durable true,
false, // auto-delete false,
false, // internal false,
false, // no-wait false,
nil, // arguments nil,
) )
if err != nil { if err != nil {
r.channel.Close()
r.conn.Close()
log.Printf("Failed to declare exchange: %v", err) log.Printf("Failed to declare exchange: %v", err)
time.Sleep(5 * time.Second) // Wait before retrying return err
continue
} }
q, err := ch.QueueDeclare( // Declare queue
r.queueName, // name _, err = r.channel.QueueDeclare(
true, // durable r.queueName,
false, // delete when unused true,
false, // exclusive false,
false, // no-wait false,
nil, // arguments false,
nil,
) )
if err != nil { if err != nil {
r.channel.Close()
r.conn.Close()
log.Printf("Failed to declare a queue: %v", err) log.Printf("Failed to declare a queue: %v", err)
time.Sleep(5 * time.Second) // Wait before retrying return err
continue
} }
err = ch.QueueBind(
// Bind queue
err = r.channel.QueueBind(
r.queueName, r.queueName,
r.routingKey, r.routingKey,
r.exchangeName, r.exchangeName,
false, false,
nil, nil,
) )
if err != nil {
r.channel.Close()
r.conn.Close()
log.Printf("Failed to bind queue: %v", err)
return err
}
msgs, err := ch.Consume( return nil
q.Name, // queue }
func (r *rabbitMQ) Consume(messageHandler func(ctx context.Context, body []byte)) {
for {
// Establish initial connection and setup
if err := r.connect(); err != nil {
log.Printf("Error setting up RabbitMQ: %v. Retrying in 5 seconds...", err)
time.Sleep(5 * time.Second) // Wait before retrying
continue
}
// Consume messages
msgs, err := r.channel.Consume(
r.queueName, // queue
"", // consumer "", // consumer
true, // auto-ack true, // auto-ack
false, // exclusive false, // exclusive
@ -94,16 +130,29 @@ func (r *rabbitMQ) Consume(ctx context.Context, messageHandler func(ctx context.
) )
if err != nil { if err != nil {
log.Printf("Failed to register a consumer: %v", err) log.Printf("Failed to register a consumer: %v", err)
r.channel.Close()
r.conn.Close()
time.Sleep(5 * time.Second) // Wait before retrying time.Sleep(5 * time.Second) // Wait before retrying
continue continue
} }
// Start message processing
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for { for {
select { select {
case msg := <-msgs: case msg, ok := <-msgs:
if !ok {
log.Println("Message channel closed, reconnecting...")
r.channel.Close()
r.conn.Close()
break // Break inner loop to reconnect
}
messageHandler(ctx, msg.Body) messageHandler(ctx, msg.Body)
case <-ctx.Done(): case <-ctx.Done():
log.Println("Consumer stopped.") log.Println("Context canceled, shutting down...")
r.channel.Close()
r.conn.Close()
return return
} }
} }