OK, after basic anti-patterns discussed in part 1 and 2 of this series, it’s time to discuss a bit more sophisticated messaging anti-patterns and how to write better messaging-oriented applications.
Using appropriate message type
So let’s start with the first principles of messaging. Why we want to use a message broker in our architecture? The most probable answer is to exchange data in asynchronous and lously-coupled way between our systems. In that terms we should see how our data are best represented in terms of messages. JMS specification defines a few types of message types to be used which I’d classify in two groups. In the first group I’ll put
ByteMessages which provides a kind of a plain-sheet in terms of what kind of data is carried in the message body. I think that you should strongly consider using only these message types as they provide a framework for loosely-coupled data exchange without introducing unnecessary complexity. Let’s cover briefly other message types and discuss what they bring to the picture:
If you haven’t already, be sure to read a post Jeff Mesnil wrote on this subject. It sums up pretty well what’s wrong
ObjectMessage type. In the nutshell:
You can get into a classloading mess as your systems need to share a classpath information as they need to be able to (de)serialize same objects. This increases coupling between the systems which we wanted to avoid in the first place.
It adds unnecessary performance penalties for serializing and transferring the whole objects, instead of only valuable data
In a similar fashion,
StreamMessage adds some semantics over the basic ByteMessages. Instead of treating all bytes equally, you can now write and read strings, integers, objects, etc. This was very valuable in times when JMS API was designed, but today in the age of all these advanced serialization frameworks, both binary (Protobuf and co) and text-based ones (Jackson and co.), I think you’ll better forget about it. Encode your data with the some of the tools you’re probably already using in your applications and transfer them using ByteMessages or TextMessages.
MapMessages are just one more example of a too specialized interface. It’s true that map (properties) collection format is commonly used, but it’s just one of many, so why depend and couple your application to it.
Additionally usage of ObjectMessages, StreamMessages and MapMessage ties your solution to the JMS land and prevents you to exchange messages with Stomp-based clients for example.
Avoid fat-messages (Blobs)
While it’s certainly possible to move large messages through message brokers, you should think twice if you want to do that. And when I say large messages, I mean gigabytes and gigabytes of data in a single message. If you find yourself with the requirement like that, ask yourself do you really need a messaging service to move this data. Broker internals are optimized to move large amount of messages and adding big blobs in the combination can cause various side effects (connection controls kicking in, blocking other clients, exhausting resources, etc).
The usual pattern suggested for this use case is that you should use some traditional transport, like FTP, for moving large data; and use messaging service to notify clients when data is ready for download (and where to find it). ActiveMQ even provides and [API which will do this for you under the cover of JMS API] (http://activemq.apache.org/blob-messages.html)
That’s it for today, choose your message type and size wisely so you don’t end up with tightly coupled systems and brokers/clients struggling with oversized messages.
I agree with your sentiment about ObjectMessages for communication between systems. On the other hand, ObjectMessage comes in very handy for communication inside a system.
Sometimes one does need reliable, recoverable asynchronous communication between different parts of one system. Provided, the JMS provider gets the classpath right, ObjectMessage is the simplest solution. Of course, even then fat messages should be avoided.
Comments are closed.