An Introduction to SpringSource's Advanced Message Queuing Protocol Support

Introduction

Message-orientated middleware products (MOMs) are at the heart of many SOA integration and ESB frameworks, providing an asynchronous means of communication between applications that is reliable, scalable and robust.

There are two fundamental parts to message queuing (hereafter "MQ"):

Messages: collections of binary or character (for instance ASCII or EBCDIC) data that have some meaning to a participating program. As in other communications protocols, storage, routing and delivery information is added to the message before transmission and stripped from the message prior to delivery to the receiving application.

Message queues: objects that store messages in an application. The queue order is generally first in first out (FIFO), and is ordered based on the receipt in the local queue, not the committing of the message from the sender. MOM systems usually support some form of prioritization, and thus by default the queue is held in priority, arrival sequence.

Messages do not depend on pure packet-based transmissions, such as TCP/IP. This allows the sending and receiving ends to be decoupled and potentially operate asynchronously.

Messages are guaranteed to be delivered once and once only, irrespective of errors, including system failures and network problems.

Broadly speaking MOM systems support two common models:

The point-to-point model: a sender posts messages to a particular queue and a receiver reads messages from the queue. Here, the sender knows the destination of the message and posts the message directly to the receiver's queue. It is analogous to an email system or voicemail. It is characterized by the following:

Only one consumer gets the message.

The producer does not have to be running at the time the consumer consumes the message, nor does the consumer need to be running at the time the message is sent.

Every message successfully processed can be acknowledged by the consumer.

The publish/subscribe model (often abbreviated to pub/sub) is analogous to an anonymous bulletin board. Messages are published to a particular message topic, and subscribers can register interest in receiving messages on a particular message topic. Neither the publisher nor the subscriber are aware of each other. The following are characteristics of this model:

Multiple consumers (or none) will receive the message.

There is a timing dependency between publishers and subscribers. The publisher has to create a subscription to which clients can subscribe. The subscriber has to remain continuously active to receive messages, unless it has established a durable subscription. In that case, messages published while the subscriber is not connected will be redistributed whenever it reconnects.

IBM's MQSeries (now WebSphere MQ), was one of the first commercial MOM products, launching in 1993. It remains the market leader with a market share typically estimated at around 30-40%. MQSeries was originally based around the point-to-point messaging model, though it now also supports publish/subscribe. Another early product, Tibco's Rendezvous, popularized the publish/subscribe model, though it too now supports point-to-point.

A number of alternative products also exist including Microsoft's MQ, as well as open-source options such as RabbitMQ and Progress Software's SonicMQ.

The Java Message Service and AMQP

Given the plethora of options for middleware messaging products, it is no surprise that Java EE defines a common API for communicating with messaging products in the form of the Java Message Service (JMS), which abstracts access to MOMs. Whilst lead by Sun, the creation of JMS was an industry effort, which saw Sun working closely with the messaging vendors throughout the process.

JMS also had a wider objective of supporting messaging as a first-class Java distributed computing paradigm equally with Remote Procedure Call based systems like CORBA and EJB. Specification lead Mark Hapner is quoted in The O'Reilly Java Message Service book as having said:

There were a number of MOM vendors that participated in the creation of JMS. It was an industry effort rather than a Sun effort. Sun was the spec lead and did shepherd the work but it would not have been successful without the direct involvement of the messaging vendors. Although our original objective was to provide a Java API for connectivity to MOM systems, this changed over the course of the work to the broader objective of supporting messaging as a first class Java distributed computing paradigm on equal footing with RPC.

The role of JMS is similar to that of JDBC; it allows a lot of different message-oriented middleware providers to be used from a common API. It has been remarkably successful, but it does have limitations. In particular the message format itself remains proprietary. In other words whilst one vendor product can be switched for another, one MOM product cannot read a message sent in the format used by another MOM product. In addition, since it is a Java-only API developers using other languages such as C++ are unable to use it.

The Advanced Message Queuing Protocol (AMQP) aims to address this. The initiative was started by JPMorgan Chase, who realized they were spending a significant proportion of their IT budget on integration. The firm then took the unusual step of creating a group of companies to work on a new model, AMQP, which launched in the summer of 2006. The primary business driver then is to lower the cost of integration.

The specification is currently in draft, with the two key versions being 0.9 and 0.10.

AMQP differs from JMS in that it defines wire-level details as well as a common API. Mark Fisher, lead of the Spring Integration team, and co-lead of the Spring AMQP project, described it as follows:

Like TCP and HTTP, AMQP's goal is communication interoperability. AMQP defines a specification for what a Message looks like "on the wire" regardless of what language or library you are using to produce and consume the messages. It also defines a domain model for messaging that includes Exchanges (for routing and pub/sub) and Queues (for buffering and delivery) as well as the basic behavior of producers and consumers (transactions, acknowledgements).

There is a need for several models, for different messaging use cases. For example XMPP is an open protocol for instant messaging at web scale. XMPP emphasises identity, presence and text streams; AMQP complements this with flexible routing, delivery, and support for binary streams.

The driver for adoption is the gradual move away from databases as the sole design center for applications. As communication and applications approach internet scale, data is needed in more than one place. Messaging is concerned with "data in motion", and messaging systems provide an abstract communication model. This is in contrast to databases, which are concerned with data at rest, and providing an abstract storage model. The use of both patterns is essential for many modern systems.

Messaging products first gained popularity over 15 years ago. They were used in financial services for broadcasting market price updates, and for integrating payment systems. Today, messaging is now seen as essential to many more systems. Sweet spots are associated with three cases:

safe integration across multiple applications written in many languages

distributed, internet scale systems especially 'cloud computing'

Messaging is also used for data routing and delivery, for IPC, for logging and social activity streams, transactions, web push, social network integration, alerts, monitoring, scheduling, work distribution, systems management, flow control, traffic management. Some messaging systems also provide "ESB" type functions such as transformation, filtering and business process orchestration.

Since AMQP aims to lower cost, it is intended to be a disruptive technology, and this includes expanding into territory more commonly covered by ESB products and message brokers. Fisher told us

One motivation for the creation of AMQP was to support a wider set of messaging patterns and use cases than JMS. One example is messaging for web clients such as Flash and JSON/RPC. Another example is content based routing. In enterprise Java, this has been a function of ESBs. However, AMQP's Exchange model enables any model for pub/sub and any set of routing rules. For example it would be possible to embed a rules engine, stream query engine, or geo-location based routing engine, inside an AMQP broker. In the case of RabbitMQ there is support for routing based on any computer program that has an AMQP client.

For cloud and internet computing, wire interoperability is very important. Protocols like HTTP and XMPP are used for cloud services APIs because the client does not need to install code from the service provider in order to access the service. For JMS, alas, this is not the case. Several companies have cloud AMQP APIs including StormMQ.

AMQP is like XMPP in being a wire protocol that can enable federations of brokers. Federation requires wire level interoperability across implementations, so JMS cannot provide this across vendors. Important web scale cases like reliable cross-data-centre integration are therefore completely proprietary, which forces companies to standardise on just one vendor, increasing lock-in. AMQP does not yet define federation in a standard way, but this could be added.

From a developer's perspective there are some key differences between JMS and AMQP. In particular JMS has two types of destinations – Topic for the publish/subscribe model, and Queue for the point-to-point model. Fisher explained that AMQP splits the routing and delivery capability of queues and topics into a 'routing' layer, called the exchange, and a buffering and delivery layer called the queue. Queues act on behalf of consumers. This means that a JMS topic can be modeled by one exchange with a queue for each subscriber.

SpringSource's AMQP Projects and RabbitMQ

VMware's SpringSource is a strong supporter of AMQP. The recently launched RabbitMQ 2.0.0, which SpringSource acquired in April this year, supports AMQP 0.9. In addition SpringSource has an independent Spring AMQP project, run outside the main Spring framework. Dr. Mark Pollack told us:

We felt it was important for this project to evolve on its own terms and not be influenced by the release schedule of the Spring Framework or any other project. While a final decision hasn't been made, [co-founder of the Spring project] Juergen Holler, had no objections to including it into the Spring Framework once it matures. There would be several advantages to this such as factoring out common code and interfaces that can be shared across Spring's JMS and AMQP support.

The Spring AMQP project has similar goals and a similar approach to SpringSource's JMS templates. Fisher told us

The primary goal of the Spring AMQP project is to support AMQP in general and RabbitMQ in particular in a way that is consistent with what developers expect from the Spring platform. So yes, we are following the same idioms and delivering familiar utilities to facilitate resource management and enable configuration via dependency-injection.

We do provide an AmqpTemplate that is very similar to Spring's JMS Template, and it even delegates to many of the same types of strategies, such as a MessageConverter. Moving up the stack, we are also focusing on a consistent approach within Spring Integration, meaning that we will be providing Channel Adapters and Gateways that are very similar to their JMS counterparts.

For many applications, a high level abstraction for messaging insulates them from vendor lock-in, enabling change and integration between existing systems such as IBM MQ and Tibco, and modern messaging systems. In the Spring tradition of adapting to change, it really is "just configuration" rather than code that will differ when switching between or combining the JMS and AMQP adapters within an application.

As well as the standard AmqpTemplate, SpringSource offers a specific RabbitTemplate for RabbitMQ. Fisher continued

The AmqpTemplate is an interface that defines common operations that would be relevant for any AMQP implementation. The RabbitTemplate is one such implementation and therefore has some details that are specific to the underlying RabbitMQ client.

For all typical operations, the AmqpTemplate operations are sufficient. If you feel the abstraction is getting in your way, you can access the underlying implementation. For example, in the RabbitTemplate, we have a callback that provides direct access to a Rabbit Channel instance.

This is another common idiom in Spring: high-level abstractions that are not simply the "lowest common denominator". Take JdbcTemplate as an example; most of the time the high-level query and update methods are sufficient, but you can access the raw JDBC Connection if necessary. We are following that same approach here.

Spring's AMQP and JMS support both feature prominently the idea of a MessageConverter which supports marshalling back and forth between JSON, Java serialization, and so on. Fisher told us:

In the AmqpTemplate, we have operations that take an instance of the generic AMQP Message object we have defined as part of the Spring AMQP core domain model, but we also provide operations that work directly with anyJava object. In the latter case, the MessageConverter strategy plays the important role of converting between those objects and Messages, where a Message contains a byte array as its body along with properties.

This is exactly the same role that Spring's MessageConverter strategy interface provides within Spring's JMS support by the way. In Spring 3.0, we introduced the XML-marshalling MessageConverter implementations for JMS, and the one that we have in Spring AMQP is very similar. One important distinction for AMQP is that, unlike JMS, you obviously cannot assume that you have Java on the other side.

Therefore, depending on out-of-the-box Java serialization is not recommended. In fact, it's typically not recommended even for JMS since it increases coupling between producers and consumers, but let's just say that it's even less recommended for AMQP. So, generic serialization is important, and two of the most common formats are XML and JSON. This is an area where we expect to see a lot of future development, so keep an eye out for more serialization support in the future.

SpringSource are also working to support other brokers, such as Qpid, in the future. Dr. Pollack told us:

The .NET version of the Spring AMQP library already has some work in this direction as it was used to help validate the Message class abstraction and the basic functionality of the AmqpTemplate class. That work was encouraging and I feel we can be successful in providing portability in .NET between Qpid and RabbitMQ in the near term.

On the Java side the situation is more complicated. The Java client API for Qpid is an implementation of the JMS API. It wouldn't make any sense to put an AMQP-ish layer on top of that API. The Qpid Java client APIs that are used to implement the JMS API are extremely low level, dealing with AMQP command frames, so it will take considerably more work to provide Java Qpid support. It would be great to get some contributions to the project in this area, so feel free to get in touch.

Erlang/OTP is an increasingly popular tool for platform services that are protocol-based or 'polyglot'. See for example MochiWeb, CouchDB, Scalaris, ejabberd … But many of these products have Erlang APIs for invoking 'core' functions more efficiently. We want developers to be able to use all these services and APIs from Spring just as easily as using any other service.

The Erlang communication support is based on Jinterface which provides a means to communicate with Erlang processes from Java applications. The Erlang.NET project serves the same role for .NET applications. Using these libraries you can invoke RPC to Erlang functions and also have the Java or .NET process present itself as an Erlang node.

Spring's Erlang communication support builds on top of Jinterface and Erlang.NET to provide an easier to use API for making RPC calls. The base APIs model Erlang data types as classes. For example the class OtpErlangFloat represents a Java float primitive type. As a result, there is a fair amount of boiler plate code to write that handles the conversion between native language types and Erlang data types. In addition, the use of checked exceptions in Jinterace adds to the verbosity of doing even the most simple of RPC calls.

Using the helper class ErlangTemplate you can have 'one liner' methods to make RPC calls that use primitive types as arguments, for example

If you need to convert between more complex data types then you can register an instance of Spring's ErlangConverter class to encapsulate and better structure that conversion logic.

Criticism of AMQP

AMQP is not without its critics. For one thing, whilst it does enjoy industry support with prominent members including Microsoft, Red Hat, Progress, Cisco, Novell and, of course, VMware, neither IBM or Tibco are currently involved.

Secondly, four years after public work started on the specification it is yet to reach 1.0 status, and the 0.10 draft of the specification has been attacked for being overly long and complex. iMatix's Pieter Hintjens argues that the short (40 page) 0-9SP1 (now called AMQP 0-9-1) should be promoted to 1.0 status. Hintjens told us 0-9-1 "is a great protocol, despite its flaws. The flaws could all be fixed."

Hintjens' view is significant since he was heavily involved in the original technical design of AMQP, along with John O'Hara and colleagues at JPMorgan Chase, and Martin Sustrik, also of iMatix. iMatix are clearly unhappy with the direction the specification has taken, and have resigned from the working group. We contacted Hintjens to get more background on the reasons for their decision:

iMatix worked for two years on the original spec, then two more years in the working group, helping as project manager and editor, and after the second release of what we (and the market, today) considered to be bogus specs (that is, AMQP/0.10 and AMQP/0.9), we were forced to stop investing in this process. It was simply too costly for us to try seriously to steer the protocol in a sane direction, and we're a small firm with limited budgets for distant face-to-face meetings and endless telephone conferences.

We tried very hard to fix the process (create a community of contributors around AMQP and turn the monolithic protocol into a ecosystem of smaller standards), and we failed. We were by far not the only team to burn out and abandon the workgroup. Cisco being the prime example, and they are clearly a much larger firm than iMatix who would never have stopped developing AMQP products if the protocol was healthy. We presented our requests for more openness and transparency repeatedly, over years, in word and in writing. To present this as a series of confrontations between iMatix and other vendors is a distortion of the facts, which are well recorded in the email list archives.

A third issue is around patents, and specifically Red Hat applying for a patent around XML based routing for AMQP. Hintjens has worked on software patents in Europe since 2005 and believes that they have a detrimental impact on innovation. As you might expect, he is strongly critical of Red Hat, describing their actions as "shocking". He went on

Red Hat's actions in patenting around AMQP were surprising given its firm vocal position over the years against software patents, and their failure to discuss this with anyone else. But in fact Red Hat take quite a lot of patents on software and business methods, and they do not grant licenses, just "promises not to sue", which are fairly weak, legally. We can assume patents form a non-trivial part of their commercial strategy. Their AMQP patent is on XML routing, which is not part of the core spec and thus not covered by any patent license. They effectively patented just outside the standard in an area where people were likely to extend. It allows them to claim on the one hand "we don't have any patents on AMQP and/or we of course license any patents under the AMQP license" while also telling prospects "we have patents on AMQP that you must license or risk infringing". Red Hat made several weird claims over the years, such as claiming to invent AMQP and then making it open.

We had at least one large client express very real concern at the patent, and while the noise abated, it didn't settle anything. People just did not want to argue in public. Privately, the claiming of patents anywhere near AMQP is a flatly hostile act but consistent with Red Hat's general approach to this technology.

Dr. Pollack's take on this was as follows

There are many patents in the area of XML routing. Certainly the threat of patent trolling is a concern to many end users and software firms even around TCP and HTTP. The motive for Red Hat’s actions was unclear at the time, leading to confusion. Red Hat issued a clarifying statement after which the blog storm abated.

What many commentators did not realize was the strength of AMQP's protections against patent abuse. To clarify:

AMQP has a strong legal underpinning that protects implementers in a number of ways. AMQP is based on intellectual property contributed by the member firms in a way that is similar to the W3C model for IPR. Also, all the specification documents are available under an open license that is similar to the IETF license.

Although the AMQP contributor agreement has not been made public it can be described as a form of 'patent treaty'. There are no royalties associated with AMQP and all the member firms have undertaken, in writing, to not sue any implementers or users of AMQP for patent infringement in relation to any patents relating to AMQP. Red Hat are one of the signatories to this agreement.

Other Public Implementations and Alternatives

As well as SpringSource, a small number of other public implementations of AMQP currently exist. These include

There is also OpenAMQ, an open-source implementation of AMQP, written in C by iMatix, but discontinued since iMatix switched its attention to its ZeroMQ product.

Hintjens told us that ZeroMQ is based on a model that

...doesn't mandate a single broker but a network of devices (queues, forwarders, proxies) that connect a set of smart clients. It's a model of communication that reflects the Internet, whereas AMQP represents perhaps Facebook or Google. Of course any network needs intermediaries, the question is whether you want and trust a single dominant intermediary, or prefer to create your own all over the place.

RabbitMQ and hence Spring AMQP work with ZeroMQ. Dr. Pollack explained that this integration code was being developed by the RabbitMQ team working with Martin Sustrik, who is a member of the ZeroMQ community and the main contributor of code to ZeroMQ.

He went on

From the RabbitMQ point of view, ZeroMQ is a messaging client providing useful network abstractions. From the ZeroMQ point of view, RabbitMQ is a drop-in messaging device for when you need a robust messaging intermediary.

The most common use case I find for transactional messaging is the need to read a message from a queue, update a database, then write a message to a queue and do all three operations (or sets of operations) atomically. The CTO of Amazon states that all their services are asynchronous and atomic. This use case matches that need.

Best efforts 1PC works in this case. It is well described in the WebSphere manual.

At transaction commit, the two-phase commit resources are prepared first using the two-phase commit protocol, and if this is successful the one-phase commit-resource is then called to commit. The two-phase commit resources are then committed or rolled back depending on the response of the one-phase commit resource.

The problem comes if you have more than one single phase committer. For instance, if the GET and PUT queues are on different instances of the message broker.

So long as RabbitMQ can participate in such transactions by offering the right interfaces, all should be fine.