Allow configuration of amqp exchange type, durability, and arguments

This commit is contained in:
Daniel Nelson 2018-06-03 16:31:11 -07:00 committed by Daniel Nelson
parent 19ec77fb32
commit 5e267c941d
4 changed files with 217 additions and 59 deletions

View File

@ -17,23 +17,36 @@ The following defaults are known to work with RabbitMQ:
[[inputs.amqp_consumer]] [[inputs.amqp_consumer]]
## AMQP url ## AMQP url
url = "amqp://localhost:5672/influxdb" url = "amqp://localhost:5672/influxdb"
## AMQP exchange
## Exchange to declare and consume from.
exchange = "telegraf" exchange = "telegraf"
## Exchange passive mode
exchange_passive = false ## Exchange type; common types are "direct", "fanout", "topic", "header", "x-consistent-hash".
# exchange_type = "topic"
## If true, exchange will be passively declared.
# exchange_passive = false
## Exchange durability can be either "transient" or "durable".
# exchange_durability = "durable"
## Additional exchange arguments.
# exchange_args = { }
# exchange_args = {"hash_propery" = "timestamp"}
## AMQP queue name ## AMQP queue name
queue = "telegraf" queue = "telegraf"
## Binding Key ## Binding Key
binding_key = "#" binding_key = "#"
## Controls how many messages the server will try to keep on the network ## Maximum number of messages server should give to the worker.
## for consumers before receiving delivery acks. prefetch_count = 50
#prefetch_count = 50
## Auth method. PLAIN and EXTERNAL are supported. ## Auth method. PLAIN and EXTERNAL are supported
## Using EXTERNAL requires enabling the rabbitmq_auth_mechanism_ssl plugin as ## Using EXTERNAL requires enabling the rabbitmq_auth_mechanism_ssl plugin as
## described here: https://www.rabbitmq.com/plugins.html ## described here: https://www.rabbitmq.com/plugins.html
# auth_method = "PLAIN" # auth_method = "PLAIN"
## Optional TLS Config ## Optional TLS Config
# tls_ca = "/etc/telegraf/ca.pem" # tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem" # tls_cert = "/etc/telegraf/cert.pem"

View File

@ -18,10 +18,13 @@ import (
// AMQPConsumer is the top level struct for this plugin // AMQPConsumer is the top level struct for this plugin
type AMQPConsumer struct { type AMQPConsumer struct {
URL string URL string
// AMQP exchange
Exchange string Exchange string `toml:"exchange"`
// Exchange passive mode ExchangeType string `toml:"exchange_type"`
ExchangePassive bool ExchangeDurability string `toml:"exchange_durability"`
ExchangePassive bool `toml:"exchange_passive"`
ExchangeArguments map[string]string `toml:"exchange_arguments"`
// Queue Name // Queue Name
Queue string Queue string
// Binding Key // Binding Key
@ -50,7 +53,11 @@ func (a *externalAuth) Response() string {
} }
const ( const (
DefaultAuthMethod = "PLAIN" DefaultAuthMethod = "PLAIN"
DefaultExchangeType = "topic"
DefaultExchangeDurability = "durable"
DefaultPrefetchCount = 50 DefaultPrefetchCount = 50
) )
@ -58,10 +65,23 @@ func (a *AMQPConsumer) SampleConfig() string {
return ` return `
## AMQP url ## AMQP url
url = "amqp://localhost:5672/influxdb" url = "amqp://localhost:5672/influxdb"
## AMQP exchange
## Exchange to declare and consume from.
exchange = "telegraf" exchange = "telegraf"
## Exchange passive mode
exchange_passive = false ## Exchange type; common types are "direct", "fanout", "topic", "header", "x-consistent-hash".
# exchange_type = "topic"
## If true, exchange will be passively declared.
# exchange_passive = false
## Exchange durability can be either "transient" or "durable".
# exchange_durability = "durable"
## Additional exchange arguments.
# exchange_args = { }
# exchange_args = {"hash_propery" = "timestamp"}
## AMQP queue name ## AMQP queue name
queue = "telegraf" queue = "telegraf"
## Binding Key ## Binding Key
@ -178,29 +198,28 @@ func (a *AMQPConsumer) connect(amqpConf *amqp.Config) (<-chan amqp.Delivery, err
return nil, fmt.Errorf("Failed to open a channel: %s", err) return nil, fmt.Errorf("Failed to open a channel: %s", err)
} }
if a.ExchangePassive == true { var exchangeDurable = true
err = ch.ExchangeDeclarePassive( switch a.ExchangeDurability {
a.Exchange, // name case "transient":
"topic", // type exchangeDurable = false
true, // durable default:
false, // auto-deleted exchangeDurable = true
false, // internal
false, // no-wait
nil, // arguments
)
} else {
err = ch.ExchangeDeclare(
a.Exchange, // name
"topic", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
} }
exchangeArgs := make(amqp.Table, len(a.ExchangeArguments))
for k, v := range a.ExchangeArguments {
exchangeArgs[k] = v
}
err = declareExchange(
ch,
a.Exchange,
a.ExchangeType,
a.ExchangePassive,
exchangeDurable,
exchangeArgs)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to declare an exchange: %s", err) return nil, err
} }
q, err := ch.QueueDeclare( q, err := ch.QueueDeclare(
@ -252,6 +271,42 @@ func (a *AMQPConsumer) connect(amqpConf *amqp.Config) (<-chan amqp.Delivery, err
return msgs, err return msgs, err
} }
func declareExchange(
channel *amqp.Channel,
exchangeName string,
exchangeType string,
exchangePassive bool,
exchangeDurable bool,
exchangeArguments amqp.Table,
) error {
var err error
if exchangePassive {
err = channel.ExchangeDeclarePassive(
exchangeName,
exchangeType,
exchangeDurable,
false, // delete when unused
false, // internal
false, // no-wait
exchangeArguments,
)
} else {
err = channel.ExchangeDeclare(
exchangeName,
exchangeType,
exchangeDurable,
false, // delete when unused
false, // internal
false, // no-wait
exchangeArguments,
)
}
if err != nil {
return fmt.Errorf("error declaring exchange: %v", err)
}
return nil
}
// Read messages from queue and add them to the Accumulator // Read messages from queue and add them to the Accumulator
func (a *AMQPConsumer) process(msgs <-chan amqp.Delivery, acc telegraf.Accumulator) { func (a *AMQPConsumer) process(msgs <-chan amqp.Delivery, acc telegraf.Accumulator) {
defer a.wg.Done() defer a.wg.Done()
@ -283,8 +338,10 @@ func (a *AMQPConsumer) Stop() {
func init() { func init() {
inputs.Add("amqp_consumer", func() telegraf.Input { inputs.Add("amqp_consumer", func() telegraf.Input {
return &AMQPConsumer{ return &AMQPConsumer{
AuthMethod: DefaultAuthMethod, AuthMethod: DefaultAuthMethod,
PrefetchCount: DefaultPrefetchCount, ExchangeType: DefaultExchangeType,
ExchangeDurability: DefaultExchangeDurability,
PrefetchCount: DefaultPrefetchCount,
} }
}) })
} }

View File

@ -21,13 +21,28 @@ For an introduction to AMQP see:
### Configuration: ### Configuration:
``` ```toml
# Configuration for the AMQP server to send metrics to # Configuration for the AMQP server to send metrics to
[[outputs.amqp]] [[outputs.amqp]]
## AMQP url ## AMQP url
url = "amqp://localhost:5672/influxdb" url = "amqp://localhost:5672/influxdb"
## AMQP exchange
## Exchange to declare and publish to.
exchange = "telegraf" exchange = "telegraf"
## Exchange type; common types are "direct", "fanout", "topic", "header", "x-consistent-hash".
# exchange_type = "topic"
## If true, exchange will be passively declared.
# exchange_passive = false
## Exchange durability can be either "transient" or "durable".
# exchange_durability = "durable"
## Additional exchange arguments.
# exchange_args = { }
# exchange_args = {"hash_propery" = "timestamp"}
## Auth method. PLAIN and EXTERNAL are supported ## Auth method. PLAIN and EXTERNAL are supported
## Using EXTERNAL requires enabling the rabbitmq_auth_mechanism_ssl plugin as ## Using EXTERNAL requires enabling the rabbitmq_auth_mechanism_ssl plugin as
## described here: https://www.rabbitmq.com/plugins.html ## described here: https://www.rabbitmq.com/plugins.html
@ -40,7 +55,7 @@ For an introduction to AMQP see:
routing_tag = "host" routing_tag = "host"
## Delivery Mode controls if a published message is persistent ## Delivery Mode controls if a published message is persistent
## Valid options are "transient" and "persistent". default: "transient" ## Valid options are "transient" and "persistent". default: "transient"
# delivery_mode = "transient" delivery_mode = "transient"
## InfluxDB retention policy ## InfluxDB retention policy
# retention_policy = "default" # retention_policy = "default"

View File

@ -26,8 +26,13 @@ type client struct {
type AMQP struct { type AMQP struct {
// AMQP brokers to send metrics to // AMQP brokers to send metrics to
URL string URL string
// AMQP exchange
Exchange string Exchange string `toml:"exchange"`
ExchangeType string `toml:"exchange_type"`
ExchangeDurability string `toml:"exchange_durability"`
ExchangePassive bool `toml:"exchange_passive"`
ExchangeArguments map[string]string `toml:"exchange_arguments"`
// AMQP Auth method // AMQP Auth method
AuthMethod string AuthMethod string
// Routing Key (static) // Routing Key (static)
@ -65,7 +70,11 @@ func (a *externalAuth) Response() string {
} }
const ( const (
DefaultAuthMethod = "PLAIN" DefaultAuthMethod = "PLAIN"
DefaultExchangeType = "topic"
DefaultExchangeDurability = "durable"
DefaultRetentionPolicy = "default" DefaultRetentionPolicy = "default"
DefaultDatabase = "telegraf" DefaultDatabase = "telegraf"
) )
@ -73,8 +82,23 @@ const (
var sampleConfig = ` var sampleConfig = `
## AMQP url ## AMQP url
url = "amqp://localhost:5672/influxdb" url = "amqp://localhost:5672/influxdb"
## AMQP exchange
## Exchange to declare and publish to.
exchange = "telegraf" exchange = "telegraf"
## Exchange type; common types are "direct", "fanout", "topic", "header", "x-consistent-hash".
# exchange_type = "topic"
## If true, exchange will be passively declared.
# exchange_passive = false
## Exchange durability can be either "transient" or "durable".
# exchange_durability = "durable"
## Additional exchange arguments.
# exchange_args = { }
# exchange_args = {"hash_propery" = "timestamp"}
## Auth method. PLAIN and EXTERNAL are supported ## Auth method. PLAIN and EXTERNAL are supported
## Using EXTERNAL requires enabling the rabbitmq_auth_mechanism_ssl plugin as ## Using EXTERNAL requires enabling the rabbitmq_auth_mechanism_ssl plugin as
## described here: https://www.rabbitmq.com/plugins.html ## described here: https://www.rabbitmq.com/plugins.html
@ -166,17 +190,28 @@ func (q *AMQP) Connect() error {
return fmt.Errorf("Failed to open a channel: %s", err) return fmt.Errorf("Failed to open a channel: %s", err)
} }
err = channel.ExchangeDeclare( var exchangeDurable = true
q.Exchange, // name switch q.ExchangeDurability {
"topic", // type case "transient":
true, // durable exchangeDurable = false
false, // delete when unused default:
false, // internal exchangeDurable = true
false, // no-wait }
nil, // arguments
) exchangeArgs := make(amqp.Table, len(q.ExchangeArguments))
for k, v := range q.ExchangeArguments {
exchangeArgs[k] = v
}
err = declareExchange(
channel,
q.Exchange,
q.ExchangeType,
q.ExchangePassive,
exchangeDurable,
exchangeArgs)
if err != nil { if err != nil {
return fmt.Errorf("Failed to declare an exchange: %s", err) return err
} }
q.setClient(&client{ q.setClient(&client{
@ -203,6 +238,42 @@ func (q *AMQP) Connect() error {
return nil return nil
} }
func declareExchange(
channel *amqp.Channel,
exchangeName string,
exchangeType string,
exchangePassive bool,
exchangeDurable bool,
exchangeArguments amqp.Table,
) error {
var err error
if exchangePassive {
err = channel.ExchangeDeclarePassive(
exchangeName,
exchangeType,
exchangeDurable,
false, // delete when unused
false, // internal
false, // no-wait
exchangeArguments,
)
} else {
err = channel.ExchangeDeclare(
exchangeName,
exchangeType,
exchangeDurable,
false, // delete when unused
false, // internal
false, // no-wait
exchangeArguments,
)
}
if err != nil {
return fmt.Errorf("error declaring exchange: %v", err)
}
return nil
}
func (q *AMQP) Close() error { func (q *AMQP) Close() error {
c := q.getClient() c := q.getClient()
if c == nil { if c == nil {
@ -292,10 +363,12 @@ func (q *AMQP) setClient(c *client) {
func init() { func init() {
outputs.Add("amqp", func() telegraf.Output { outputs.Add("amqp", func() telegraf.Output {
return &AMQP{ return &AMQP{
AuthMethod: DefaultAuthMethod, AuthMethod: DefaultAuthMethod,
Database: DefaultDatabase, ExchangeType: DefaultExchangeType,
RetentionPolicy: DefaultRetentionPolicy, ExchangeDurability: DefaultExchangeDurability,
Timeout: internal.Duration{Duration: time.Second * 5}, Database: DefaultDatabase,
RetentionPolicy: DefaultRetentionPolicy,
Timeout: internal.Duration{Duration: time.Second * 5},
} }
}) })
} }