Currently, I have a single queue with a single consumer that process messages in order (FIFO).
We need to scale out our application and add more consumers... at least two, maybe more.
In our application FIFO is necessary, as the messages represent operations on objects.
Think of building a car... it must be assembled in a specific order.
According to tutorial #2 on works queues...
"By default, RabbitMQ will send each message to the next consumer,
in sequence.
On average every consumer will get the same number of
messages.
This way of distributing messages is called round-robin."
Round-robin will not work for us in this case, as the queue may have the following elements
(head of queue, next to process)
1 - Car serial # 100, insert engine
2 - Car serial # 100, mount hood
3 - Car serial # 101, insert engine
4 - Car serial # 102, insert engine
In this case, with 2 consumers named A and B, message #1 would get dispatched to Consumer A, and message #2 would get dispatched to Consumer B.
But, inserting the engine must be guaranteed to complete processing before mounting the hood, that is I must make sure message #1 is processed before message #2.
If they are processed by different consumers, I have no way to guarantee that will happen.
My idea to solve this problem would be to dispatch messages to a consumer based on the last digit of the serial number.
For example
- 1 consumer, all digits 0-9 go to single consumer
- 2 consumers, digits 0-4 go to consumer A, digits 5-9 go to consumer B
- 3 consumers (you get the idea)
I have seen a few proposed solutions
1 - write a "man-in-the-middle" type client that reads messages from the original queue and publishes them to separate queues based on serial number
I think this is probably the easiest to write, but I need to make sure that messages are not building up in the separate workers queues in case one of them fails.
In the event of failure of one consumer, we must switch back to the single consumer processing all messages so they stay in order.
We are already using prefetch, but I am not sure if prefetch would be safe here?
I would need to pass the ack backward from the consumer, to the "man-in-the-middle", then all the way back to the main queue, I think.
2 - it might be possible to write a custom exchange that distributes messages to the proper queue
However, again I need to make sure messages do not get backed up in the queue, for same reasons as #1
3 - is there a way to customize how messages are distributed from the queue to the consumers?
The tutorial says "by default" but I couldn't find info about changing the default or if there are settings to adjust that.
What classes/code would I need to modify / extend to make a plugin for such a change?
If anyone knows of a plugin or other software (open source) that offers this, that would work great.
I don't know Erlang but could learn if necessary.
Thank you! :)
- Doug