Richard Bucker

Proper use of a MQ designs

Posted at — Jan 27, 2012

Bus, as a term referring to hardware/software components has been around long enough that many noobs have no idea of it’s origin or how to use it properly or when to use it.In modern computing the bus has it’s origin in hardware. It was not always like that. Engineers went back and forth between direct connected components and bus architecture until the 1980s when IBM introduced BusMaster architecture in the PS/2. Things remained stagnant for a few years until there was yet another resurgence of direct connected hardware.Memory I/O performance was increased by moving cache directly into the CPU, connecting the CPU to RAM via DMA type access.1Disk I/O performance was increased when the disk controllers were allowed to talk directly to the systems RAM.So it’s no wonder now that MQs are becoming easier to deploy that they are becoming the connective tissue between components rather than direct connections or API calls.Old school programmers remember MQs like IBM’s very well when main memory was very expensive and it was cheaper to page services (SOA) in and out of main system memory when an actual event was there and ready for processing. The complete static application codebase could not fit in memory and implementing smaller services was practical.MQs were not implemented or made popular on PCs until recently with the advent of J2EE, SOA, and the notion of an application bus. The idea being that services could be distributed across a single machine, a cluster of machines or a WAN cluster of machines.One of my earlier recollections was when I worked for IBM and A/IX team on the A/IX microkernel.Now we are experiencing a renaissance of a sort where software/system architects are installing MQs in every corner of their application design. I’m clueless as to why. I’m constantly wrestling with my own designs wondering where the value is and where the inevitable costs are. And one thing for sure is that there always tradeoffs. But here’s the thing and for me it’s starting to become a rule instead of a thing.If it takes more time and/or more code to MQ an event [or the event contents is relatively large] to a service then the code that makes up that service should be statically linked rather than treated as a service because you are just generating heat and not money.For example, in modern programming languages the first target application is typically hello world. And in most MQ implementations the first service is typically echo or add. Sadly, if the rule were implemented then these two services would never be written and we would have to think about more complex services to demonstrate the design (echo and add are so simple that they fool most managers).In the simple case:<img class=“aligncenter size-full wp-image-765” title=“simple add” src=“http://rbucker.files.wordpress.com/2012/01/simple-add.png" alt=” width=“645” height=“265” />There is very little overhead here. The contract between the client and the add class/service is clear. The code is small enough, the data is small enough, that statically linking the code makes the most possible sense. Consider that the service that the service provides is but a few assembly instructions once you get past the instruction stack and memory fetches.But if you’ve had a sip from the bug-juice then you see the world this way:<img class=“aligncenter size-full wp-image-766” title=“simple add plus” src=“http://rbucker.files.wordpress.com/2012/01/simple-add-plus.png" alt=” width=“645” height=“415” />In this case the client thinks it’s calling the add function locally, and the add function thinks it’s being called locally, the fact is that may not be the case. The remote add stub and the MQ may be on the same machine or even CPU core in the same box but it might not be.In fact the stub has a certain amount of overhead just because it exists. Then there is the communication overhead. (a remote connection across a WAN can take from 100 to 300ms on a good day), The marshaling of the data from one system to another has some overhead too. (back in the day of the original RPC there was a stub generator that would simply massage the endian-ness of the data so that it was cross platform; now things are much more complicated). The actual add() is only going to take one or two CPU clock cycles; which cannot be measured in ms. So for the sake of some grandiose style guide we have added huge amounts of overhead.In a recent design I implemented an MQ/service-bus in order to handle the impedance mismatch between incoming connections using epoll, a single threads web server, and the need to be able to handle high transaction volumes.<img class=“aligncenter size-full wp-image-767” title=“mousetrap” src=“http://rbucker.files.wordpress.com/2012/01/mousetrap.png" alt=” width=“645” height=“122” />In this case the service does a lot more than just add two numbers. The message from the end-user client needs to be parsed(1), some data needs to be decrypted(2), some initial decisioning needs to be performed(3), and then the transaction needs to be reformatted(4) and directed to a 3rd party for processing(4&5).  When the 3rd party completes the transaction, then the response needs to be parsed(6), a few more decisions made(7), and then a response is assembled for the end-user(8). And then the response is sent back to the client through the same path(9).What makes this different than the add() service is that it’s performing real work in the form of the service. If I took all of the work-units that the service performed and split the service into the sub-parts then I could potentially have hundreds of services, each with 100-300ms of communication overhead. I’ve identified 9 possible steps in the transaction… and at 100ms per steps that’s almost a full second.I’m trying to find a silver lining for a fine-grained SOA but I cannot. The work performed by the service is sync not async and therefore the product of the non-essential MQ is heat.Two examples;1) Google is working on GO, a new programming language. Rob Pike and Co have been describing Google’s code base. The code covers several programming languages and it is considered a monolith. GO is best used when it’s statically linked; I do not recall if it even supports dynamic libs.2) Depending on your magnification the study of MQ starts with the hardware and some sub-components. Then  firmware, then the OS and it’s device drivers, and continues to build out. This looks a lot like a Mandelbrot image of sorts.Our predecessors selected what was a service and what was a direct connect very carefully. Mostly based on ROI and the cost of gold. The same decisions are true today.