169 lines
4.8 KiB
Go
169 lines
4.8 KiB
Go
package rabbitmq_amqp
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/Azure/go-amqp"
|
|
)
|
|
|
|
//func (c *ConnUrlHelper) UseSsl(value bool) {
|
|
// c.UseSsl = value
|
|
// if value {
|
|
// c.Scheme = "amqps"
|
|
// } else {
|
|
// c.Scheme = "amqp"
|
|
// }
|
|
//}
|
|
|
|
type AmqpConnection struct {
|
|
Connection *amqp.Conn
|
|
management *AmqpManagement
|
|
lifeCycle *LifeCycle
|
|
session *amqp.Session
|
|
}
|
|
|
|
// NewPublisher creates a new Publisher that sends messages to the provided destination.
|
|
// The destination is a TargetAddress that can be a Queue or an Exchange with a routing key.
|
|
// See QueueAddress and ExchangeAddress for more information.
|
|
func (a *AmqpConnection) NewPublisher(ctx context.Context, destination TargetAddress, linkName string) (*Publisher, error) {
|
|
destinationAdd := ""
|
|
err := error(nil)
|
|
if destination != nil {
|
|
destinationAdd, err = destination.toAddress()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = validateAddress(destinationAdd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
sender, err := a.session.NewSender(ctx, destinationAdd, createSenderLinkOptions(destinationAdd, linkName, AtLeastOnce))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newPublisher(sender, destinationAdd != ""), nil
|
|
}
|
|
|
|
// NewConsumer creates a new Consumer that listens to the provided destination. Destination is a QueueAddress.
|
|
func (a *AmqpConnection) NewConsumer(ctx context.Context, destination *QueueAddress, linkName string) (*Consumer, error) {
|
|
destinationAdd, err := destination.toAddress()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = validateAddress(destinationAdd)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
receiver, err := a.session.NewReceiver(ctx, destinationAdd, createReceiverLinkOptions(destinationAdd, linkName, AtLeastOnce))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newConsumer(receiver), nil
|
|
}
|
|
|
|
// Dial connect to the AMQP 1.0 server using the provided connectionSettings
|
|
// Returns a pointer to the new AmqpConnection if successful else an error.
|
|
// addresses is a list of addresses to connect to. It picks one randomly.
|
|
// It is enough that one of the addresses is reachable.
|
|
func Dial(ctx context.Context, addresses []string, connOptions *amqp.ConnOptions) (*AmqpConnection, error) {
|
|
conn := &AmqpConnection{
|
|
management: NewAmqpManagement(),
|
|
lifeCycle: NewLifeCycle(),
|
|
}
|
|
tmp := make([]string, len(addresses))
|
|
copy(tmp, addresses)
|
|
|
|
// random pick and extract one address to use for connection
|
|
for len(tmp) > 0 {
|
|
idx := random(len(tmp))
|
|
addr := tmp[idx]
|
|
// remove the index from the tmp list
|
|
tmp = append(tmp[:idx], tmp[idx+1:]...)
|
|
err := conn.open(ctx, addr, connOptions)
|
|
if err != nil {
|
|
Error("Failed to open connection", ExtractWithoutPassword(addr), err)
|
|
continue
|
|
}
|
|
Debug("Connected to", ExtractWithoutPassword(addr))
|
|
return conn, nil
|
|
}
|
|
return nil, fmt.Errorf("no address to connect to")
|
|
}
|
|
|
|
// Open opens a connection to the AMQP 1.0 server.
|
|
// using the provided connectionSettings and the AMQPLite library.
|
|
// Setups the connection and the management interface.
|
|
func (a *AmqpConnection) open(ctx context.Context, addr string, connOptions *amqp.ConnOptions) error {
|
|
|
|
if connOptions == nil {
|
|
connOptions = &amqp.ConnOptions{
|
|
// RabbitMQ requires SASL security layer
|
|
// to be enabled for AMQP 1.0 connections.
|
|
// So this is mandatory and default in case not defined.
|
|
SASLType: amqp.SASLTypeAnonymous(),
|
|
}
|
|
}
|
|
|
|
//connOptions.HostName is the way to set the virtual host
|
|
// so we need to pre-parse the URI to get the virtual host
|
|
// the PARSE is copied from go-amqp091 library
|
|
// the URI will be parsed is parsed again in the amqp lite library
|
|
uri, err := ParseURI(addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
connOptions.HostName = fmt.Sprintf("vhost:%s", uri.Vhost)
|
|
|
|
conn, err := amqp.Dial(ctx, addr, connOptions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.Connection = conn
|
|
a.session, err = a.Connection.NewSession(ctx, nil)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = a.management.Open(ctx, a)
|
|
if err != nil {
|
|
// TODO close connection?
|
|
return err
|
|
}
|
|
|
|
a.lifeCycle.SetState(&StateOpen{})
|
|
return nil
|
|
}
|
|
|
|
func (a *AmqpConnection) Close(ctx context.Context) error {
|
|
err := a.management.Close(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = a.Connection.Close()
|
|
a.lifeCycle.SetState(&StateClosed{})
|
|
return err
|
|
}
|
|
|
|
// NotifyStatusChange registers a channel to receive getState change notifications
|
|
// from the connection.
|
|
func (a *AmqpConnection) NotifyStatusChange(channel chan *StateChanged) {
|
|
a.lifeCycle.chStatusChanged = channel
|
|
}
|
|
|
|
func (a *AmqpConnection) State() LifeCycleState {
|
|
return a.lifeCycle.State()
|
|
}
|
|
|
|
// *** management section ***
|
|
|
|
// Management returns the management interface for the connection.
|
|
// The management interface is used to declare and delete exchanges, queues, and bindings.
|
|
func (a *AmqpConnection) Management() *AmqpManagement {
|
|
return a.management
|
|
}
|
|
|
|
//*** end management section ***
|