github.com/fredmanre/go-rabbitmq-client@v1.0.7/go-rabbitmq/client.go (about)

     1  package rabbit
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"time"
     7  
     8  	amqp "github.com/rabbitmq/amqp091-go"
     9  )
    10  
    11  // RabbitMQClient contains the basic objects for a AMQ connection
    12  type RabbitMQClient struct {
    13  	Connection *amqp.Connection
    14  	Channel    *amqp.Channel
    15  	Reception  struct {
    16  		Queue amqp.Queue
    17  	}
    18  	Dispatch struct{}
    19  	Err      chan error
    20  }
    21  
    22  // RabbitConnection Reconnection config
    23  type RabbitConnection struct {
    24  	username            string
    25  	password            string
    26  	host                string
    27  	port                int `default:"5672"`
    28  	ReconnectionTimeout int `default:"5"`
    29  	SetupQueue          RabbitSetupQueue
    30  }
    31  
    32  // RabbitSetupQueue Queue config
    33  type RabbitSetupQueue struct {
    34  	exchange       string
    35  	queueName      string
    36  	autoDelete     bool
    37  	routingKeys    []string
    38  	queueIsDurable bool
    39  }
    40  
    41  var RbConn RabbitConnection
    42  
    43  // InitMeConn rabbit params - RabbitParameters
    44  func (rbp *RabbitConnection) InitMeConn(username, password, host string, port int) {
    45  	rbp.username, rbp.password, rbp.host, rbp.port = username, password, host, port
    46  }
    47  
    48  // InitMeSetupQueue Rabbit SetUp queue configuration for reconnection
    49  func (rbp *RabbitConnection) InitMeSetupQueue(exchange, queueName string, autoDelete, isDurable bool, routingKeys []string) {
    50  	rbp.SetupQueue.exchange, rbp.SetupQueue.queueName = exchange, queueName
    51  	rbp.SetupQueue.autoDelete, rbp.SetupQueue.queueIsDurable, rbp.SetupQueue.routingKeys = autoDelete, isDurable, routingKeys
    52  }
    53  
    54  // GetQueueName RabbitMQClient_GetQueueName return the queue name for a receiver
    55  func (a *RabbitMQClient) GetQueueName() string {
    56  	return a.Reception.Queue.Name
    57  }
    58  
    59  // StartConnection RabbitMQClient_StartConnection Starts the connection with rabbitMQ server.
    60  // Dials up and creates a channel
    61  func (a *RabbitMQClient) StartConnection(username, password, host string, port int) error {
    62  
    63  	a.Err = make(chan error)
    64  	RbConn.InitMeConn(username, password, host, port)
    65  	url := fmt.Sprintf("amqp://%s:%s@%s:%d/", username, password, host, port)
    66  	conn, err := amqp.Dial(url)
    67  	if err != nil {
    68  		return fmt.Errorf("amqp dial failure: %s", err)
    69  	}
    70  
    71  	a.Connection = conn
    72  	// we declare a channel to be used for the reconnection process
    73  	go func() {
    74  		closeErr := <-a.Connection.NotifyClose(make(chan *amqp.Error)) // we listen to notify if the connection is closed
    75  		a.Err <- fmt.Errorf("disconnected from rabbitMQ: %s", closeErr)
    76  	}()
    77  
    78  	errCh := a.CreateChannel()
    79  	if errCh != nil {
    80  		return fmt.Errorf("Couldn't create a channel in start connection err: %s", errCh)
    81  	}
    82  	return nil
    83  }
    84  
    85  // CreateChannel RabbitMQClient_CreateChannel creates a channel and saves it in struct
    86  func (a *RabbitMQClient) CreateChannel() error {
    87  	ch, err := a.Connection.Channel()
    88  	if err != nil {
    89  		return fmt.Errorf("channel creating failure: %s", err)
    90  	}
    91  	a.Channel = ch
    92  	return nil
    93  }
    94  
    95  // Reconnect Manage reconnection to rabbitMQ
    96  // for startConnection: username, password, host & port
    97  // for SetupQueues: queueName, queueIsDurable, autoDelete, routingKeys, exchange
    98  func (a *RabbitMQClient) Reconnect() error {
    99  
   100  	time.Sleep(time.Duration(RbConn.ReconnectionTimeout))
   101  	fmt.Println("Recconnecting with params ", RbConn)
   102  	RbConn.ReconnectionTimeout += 5
   103  	if err := a.StartConnection(RbConn.username, RbConn.password, RbConn.host, RbConn.port); err != nil {
   104  		return err
   105  	}
   106  	if err := a.SetupQueues(
   107  		RbConn.SetupQueue.queueName, RbConn.SetupQueue.queueIsDurable,
   108  		RbConn.SetupQueue.autoDelete, RbConn.SetupQueue.routingKeys,
   109  		RbConn.SetupQueue.exchange); err != nil {
   110  		return err
   111  	}
   112  	return nil
   113  
   114  }
   115  
   116  // SetupQueues RabbitMQClient_SetupQueues Declares and binds a queue to an exchange
   117  func (a *RabbitMQClient) SetupQueues(queueName string, queueIsDurable, autoDelete bool, routingKeys []string, exchange string) error {
   118  	q, err := a.Channel.QueueDeclare(
   119  		queueName,      // name
   120  		queueIsDurable, // durable
   121  		autoDelete,     // delete when un used
   122  		false,          // exclusive
   123  		false,          // no-wait
   124  		nil,            // arguments
   125  	)
   126  	if err != nil {
   127  		return fmt.Errorf("failed to declare a queue with name: %s, err: %s", queueName, err)
   128  	}
   129  	RbConn.InitMeSetupQueue(exchange, queueName, autoDelete, queueIsDurable, routingKeys) // params for reconnection queue
   130  	for _, key := range routingKeys {
   131  		errBind := a.Channel.QueueBind(queueName, key, exchange, false, nil)
   132  		if errBind != nil {
   133  			return fmt.Errorf("failed to Bind a queue err: %s", errBind)
   134  		}
   135  	}
   136  
   137  	a.Reception.Queue = q
   138  	return nil
   139  }
   140  
   141  // StartReceiver RabbitMQClient_StartReceiver Starts a rabbit MQ receiver with the passed configuration, returns a channel
   142  // that will receive the messages, along with the connection and channel instance
   143  func (a *RabbitMQClient) StartReceiver(queueName string, isDurable, autoDelete bool, routingKeys []string, exchanges interface{}, consumerTag string) (<-chan amqp.Delivery, error) {
   144  	switch exchangeData := exchanges.(type) {
   145  	case []string:
   146  		for _, exchange := range exchangeData {
   147  			err := a.SetupQueues(
   148  				queueName, isDurable, autoDelete, routingKeys, exchange,
   149  			)
   150  			if err != nil {
   151  				return make(chan amqp.Delivery), fmt.Errorf("failed to setup queue err: %s", err)
   152  			}
   153  		}
   154  	case string:
   155  		err := a.SetupQueues(
   156  			queueName, isDurable, autoDelete, routingKeys, exchangeData,
   157  		)
   158  		if err != nil {
   159  			return make(chan amqp.Delivery), fmt.Errorf("failed to setup queue err: %s", err)
   160  		}
   161  
   162  	default:
   163  		return make(chan amqp.Delivery), fmt.Errorf("failed to declare exchange due to wrong type in var %v", exchanges)
   164  	}
   165  	messages, err := a.Channel.Consume(
   166  		queueName,   // queue
   167  		consumerTag, // consumer
   168  		true,        // auto-ack
   169  		false,       // exclusive
   170  		false,       // no-local
   171  		false,       // no-wait
   172  		nil,         // args
   173  	)
   174  	if err != nil {
   175  		return make(chan amqp.Delivery), fmt.Errorf("failed to register a consumer: %s", err)
   176  	}
   177  	return messages, nil
   178  }
   179  
   180  // SetupDispatcher RabbitMQClient_SetupDispatcher Declares the exchanges to be used to deliver messages
   181  func (a *RabbitMQClient) SetupDispatcher(exchange, exchangeType string, isDurable, autoDelete bool) error {
   182  	if err := a.Channel.ExchangeDeclare(
   183  		exchange,     // name
   184  		exchangeType, // type
   185  		isDurable,    // durable
   186  		autoDelete,   // auto-deleted
   187  		false,        // internal
   188  		false,        // noWait
   189  		nil,          // arguments
   190  	); err != nil {
   191  		return fmt.Errorf("exchange declare: %s", err)
   192  	}
   193  	return nil
   194  }
   195  
   196  // SendMessage RabbitMQClient_SendMessage Deliver the message to the specified exchange, if exchange not created this will
   197  // throw an error
   198  func (a *RabbitMQClient) SendMessage(exchange, routingKey string, message interface{}) error {
   199  	// non blocking channel - if there is no error will go to default where we do nothing
   200  	select {
   201  	case err := <-a.Err:
   202  		if err != nil {
   203  			a.Reconnect()
   204  		}
   205  	default:
   206  	}
   207  
   208  	messageBody, err := json.Marshal(message)
   209  	if err != nil {
   210  		return fmt.Errorf("couldn't marshal the map to an slice of bytes")
   211  	}
   212  
   213  	if err = a.Channel.Publish(
   214  		exchange,   // publish to an exchange
   215  		routingKey, // routing to 0 or more queues
   216  		false,      // mandatory
   217  		false,      // immediate
   218  		amqp.Publishing{
   219  			Headers:         amqp.Table{},
   220  			ContentType:     "text/plain",
   221  			ContentEncoding: "",
   222  			Body:            messageBody,
   223  			DeliveryMode:    amqp.Transient, // 1=non-persistent, 2=persistent
   224  			Priority:        0,              // 0-9
   225  		},
   226  	); err != nil {
   227  		return fmt.Errorf("exchange publish: %s", err)
   228  	}
   229  	return nil
   230  }