Comming from a Java background, when developing services connected by JMS I used to process messages and distinguish them by checking their type, e.g (simplified):
Object object = myQueue.consume();
if (object instanceof MessageA) {
processMessageA((MessageA) object)
} else if (object instanceof MessageB) {
processMessageB((MessageB) object)
}...
So now I am building a messaging front-end for some Python modules in RabbitMQ (topic communication). I am planing on using one queue for each consumer-module to which different messages will arrive.
I have almost everything but I am still struggling with the processing (consuming) of messages. How would you distinguish between message type?
I thought of having custom JSON headers, but I don't know if this is correct.
When programming in python, especially for people who come from OO languages, it is important to keep two principles in mind.
First, Python is not an OO language, it only supports classes and objects. The most pythonic way is usually one that does not rely on typing and\or classes.
Second, there is the all-important "Zen of Python". This set of ideas dictates most of the way the Python language itself is built, but and also provide for those of us who program in it. Among these ideas there are two that you should strive to fulfill always.
Using these ideas I'll try to show what I believe the best way is, that way indeed uses JSON headers. Being explicit means that we should plainly state what we're trying to do, and being simple means that we should write it down where it is most logical for it to be. I would argue that this directly points at the type being in the JSON itself to be the most explicit and simple way of implementing this idea. Furthermore, I assume there is a difference between your message types, and not stating the type in the header would require you to write some code to distinguish between then based on some obscure difference, again defeating the concept of explicitness.
To close off, you should remember that anything in python is an object, including functions, which means that you can use a map as the operation dictionary:
message_to_action_map = {
'typeA': functionA,
'typeB': functionB
}
def consumer_callback(msg):
# In Python, RabbitMQ works by push and not by pull
process = message_to_action_map[msg['type']]
process(msg)
This allows you to have one place where all your code paths are explicitly specified (this is also called the Strategy pattern). It also has the added bonus of you never having to change the actual processing code.
In short, I believe that using message headers is indeed the most pythonic way to tell the messages apart, as it is both simpler and more explicit that any other way.