Messaging, Messaging, Messaging

This is the second in the sequence of blog posts regarding some of experiences in creating Trading and eCommerce platforms at Adaptive. The previous post can be found here.

In this post, we discuss the Middleware that generally lies at the heart of these platforms.

There are numerous Message Oriented Middleware (MOM) products, both open source and commercial offerings. At Adaptive, we tend to work in the low-latency, high-throughput space where commercial MOMs are used almost exclusively. We generally use MOM for two use-cases; the first is for enterprise messaging, the second is for web push.

Enterprise Messaging

Enterprise messaging includes integration with various systems dotted around the bank that expose APIs over middleware. It also includes service-to-service interactions within our platforms where those services have dependencies on each other.

Open source offerings that we have used include ActiveMQ, RabbitMQ and a few others. Commercial offerings we have used include Tibco RV, Tibco EMS, Ultra Messaging (formerly LBM and 29West) and Universal Messaging (formerly Nirvana).

Web Push

As previously discussed, there is a general trend in Trading and eCommerce UIs away from thick clients to Web and Mobile platforms. This raises a problem for platforms where you need to push live data to clients. The web was designed to operate in a request/response manner as embodied in the HTTP verbs GET, POST, PUT and DELETE. Over the years, various strategies have been employed to do this. With the advent of AJAX, polling for data became possible, but with a tradeoff between maximum latency and performance. Then came COMET and long-polling, which improved matters, but ultimately had scalability issues.

The ultimate solution to this issue comes with WebSockets, introduced by the HTML5 standard. WebSockets are a huge win for building the types of applications we do. They allow a willing client and server to upgrade from HTTP1.1 to a full-duplex, binary connection. When this is possible, it gives excellent latency, throughput and scalability. However, in corporate environments, one finds that older browsers IE6, IE8 are the norm. Sadly, these do not support WebSockets. In addition to this, often corporate networks have various appliances (Reverse Proxies, Load Balancers, Firewalls) that do not support them either. It is therefore necessary to employ various fallback mechanisms. Several open source and commercial products implement this strategy. As we operate in the high performance and low latency space, we tend to use products from commercial vendors like Universal Messaging and Kaazing.

It's all just Messaging

An interesting observation is that, if you use MOM for enterprise messaging and a WebSocket gateway for your web and mobile messaging, then the style of interaction between enterprise, web and mobile clients starts to look very similar. Further to this, we have found that with web and mobile clients, once you have established a full duplex, binary connection, in addition to routing your push messaging traffic through this connection, great performance and scalability increases can be gained by tunnelling your pull traffic through this connection also.

A Common Abstraction

Once we've come to the realisation that both enterprise and web messaging are fundamentally the same thing, we can define a common messaging abstraction. This abstraction allows us to build higher-level messaging protocols and also allows us to integrate with different MOM products by simply implementing a few interfaces with a small surface area.

The commonality to all MOMs is the ability to establish a connection and publish and subscribe Messages. These Messages should be able to carry arbitrary, binary content and also hold metadata in order to convey information about their content. The following sections define a sample set of interfaces using pseudocode and provide a description of the purpose of each one.

Message

The fundamental interface of the abstraction is a Message. A message consists of a set of MessageProperties and a binary payload.


    // Generic Message abstraction
    interface Message {
        // Get property dictionary
        MessageProperties getProperties();
        // Get binary payload
        byte[] getPayload();    
    }

The MessageProperties interface is a strongly typed, key-value store that can be used to store dynamically typed data. It is recursive in nature, meaning that you can store arbitrarily complex object graphs. We generally use these properties for metadata like correlation IDs.


    interface MessageProperties {
        // Setters by type
        void set(String key, String value);
        void set(String key, int value);
        void set(String key, long value);
        void set(String key, double value);
        void set(String key, boolean value);
        void set(String key, MessageProperties value);
        // Getters by type
        String getString(String key);
        int getInt(String key);
        long getLong(String key);
        double getDouble(String key);
        boolean getBoolean(String key);
        MessageProperties getProperties(String key);
        // Enumerate keys
        Iterable getKeys();
    }

The MessageFactory interface is used to create a Message. When creating a Message, one can optionally supply a binary payload. The payload is generally used for passing around serialised object payloads in a variety of formats.

   
    interface MessageFactory {
        // Create a Message with optional payload
        Message create(byte[] payload = null);
    }

Session

The Session interface represents the connection to the middleware. Using this interface you can connect and disconnect. This is also the entry point for obtaining the MessageFactory, Publishers and Subscribers. Publishers and Subscribers are addressed so messages can be organised into categories.


    // Generic Session abstraction
    interface Session {
        // Connect session
        void connect();
        // Disconnect session
        void disconnect();
        // Get factory for creating messages
        MessageFactory getMessageFactory();
        // Find a Subscriber for address
        Subscriber findSubscriber(String address);
        // Find a Publisher for address
        Publisher findPublisher(String address);
    }

Publisher

The Publisher interface is used to publish Messages to a particular address.


    // Generic Publisher abstraction
    interface Publisher {
        // Publish message
        void publish(Message message);
    }

Subscriber

The Subscriber interface is used to consume Messages from a particular address. Consumption is done asynchronously by supplying a callback interface. A Subscription instance is returned.


    // Generic Subscriber abstraction
    interface Subscriber {
        // Subscribe for messages
        Subscription subscribe(MessageHandler messageHandler);
    }

The MessageHandler interface has a single callback method that is invoked with each Message to be consumed.


    // Callback interface for Messages
    interface MessageHandler {
        // Called-back on Message
        void onMessage(Message message);    
    }

The Subscription interface is used for freeing up resources. This is done by calling the unsubscribe method.


    // Generic interface for freeing resources
    interface Subscription {
        // Free resource
        void unsubscribe();
    }

Summary

In this post, we have discussed both Enterprise and Web MOM and concluded that they are actually quite similar. Having made that realisation, we defined a common set of abstraction interfaces. Having such an abstraction can be very useful as we will see when we define some higher-level messaging protocols on top of it.

The next post can be found here.

 

John Marks

CCO and co-founder,
Adaptive Financial Consulting



×

Contact us

    By pressing "Send" I agree that I am happy to be contacted by Adaptive Financial Consulting. I can unsubscribe at any time. You can read more about our privacy policy here.