INFO: What is the rationale behind setupExchangesAndQueues
zabullet opened this issue · comments
setupExchangesAndQueues makes a lot of assumptions about the setup of the queues and naming conventions.
For instance that a queue has a a dead-letter exchange or queue and that it is exactly named -dead-letter.
// regular queue
if _, err = ch.QueueDeclare(c.QueueName, true, false, false, false,
amqp.Table{
"x-dead-letter-exchange": deadLetterExchangeName,
}); err != nil {
return failOnError(err, "Failed to declare a queue:"+c.QueueName)
}
Assuming an existing queue with a dead letter exchange other than -dead-letter (for instance mine is already called -dlx)...the above will fail.
I might be missing something, but couldn't this rather be done via config?
zb.
@zabullet yes, you are right, we made some assumptions for two reasons (at least):
- We needed conventions as we have build huge part of the company on
eventbus
pattern with multiple languages and technologies and managing different naming conventions would be harder to maintain. - We initially had made this project internally and when we had found out that it can bring some value to the community, we open sourced it. So any PRs making it more generic and more configurable are more than welcome :)
I'm making an attempt to make it more configurable in terms of topology, but I have a question.
The explicit mapping of config.RabbitEntry to rabbitmq.Consumer seems unnecessary given the benefit you gain (slightly looser coupling, perhaps?).
My proposal is to extend RabbitEntry to the following
// RabbitArgumentsPair describes a rabbitMQ bind or declare argument
type RabbitArgumentsPair struct {
Key string `json:"key"`
Value string `json:"value"`
}
// RabbitTopographyItem describes an element of the rabbit topography to set up
type RabbitTopographyItem struct {
Name string `json:"name"`
Action string `json:"action"`
Type string `json:"type"`
Kind string `json:"kind"`
Routekey string `json:"routekey"`
To string `json:"to"`
Arguments *[]RabbitArgumentsPair `json:"args"`
}
// RabbitEntry RabbitMQ mapping entry
type RabbitEntry struct {
Type string `json:"type"`
Name string `json:"name"`
ConnectionURL string `json:"connection"`
ExchangeName string `json:"topic"`
QueueName string `json:"queue"`
RoutingKey string `json:"routing"`
RabbitTopography *[]RabbitTopographyItem `json:"rabbittopography"`
}
This is starting to be quite a complex structure which I don't see the benifit of duplicating in the RabbitMQ namespace.
How averse would you be if we did something like this?
// Consumer implementation or RabbitMQ consumer
type Consumer struct {
config config.RabbitEntry
}
// parameters for starting consumer
type workerParams struct {
forwarder forwarder.Client
msgs <-chan amqp.Delivery
check chan bool
stop chan bool
conn *amqp.Connection
ch *amqp.Channel
}
// CreateConsumer creates consumer from string map
func CreateConsumer(entry config.RabbitEntry) consumer.Client {
return Consumer{entry}
}
// Name consumer name
func (c Consumer) Name() string {
return c.config.Name
}
It doesn't appear to be a 1-way door or am I missing something?
Additionally, what about defining the config and mapping more generally and having the concrete types handle the unmarshalling?
// Entry Generic config structure
type Entry struct {
Type string `json:"type"`
Name string `json:"name"`
Config json.RawMessage `json:"config"`
}
with pair defined as
type pair struct {
Source config.Entry `json:"source"`
Destination config.Entry `json:"destination"`
}
I'll play around a bit here and do a spike for you to comment on
I'm going to close this. I've added a development branch on my fork.