All The World's A Stage

To go right to the other side of the size scale, the messaging model hasn’t just made waves in terms of networked applications, but has also had an impact within individual programs.

Back in 1973, Carl Hewitt published a paper on the idea of an Actor Model, imagining systems where thousands of small processors with their own memory and own resources would interact to solve problems. He defined a model where each processor would be an actor with one or more addresses. Actors could send and receive messages and updated their internal state based only on those messages.

Independently, in 1978 Tony Hoare, the inventor of QuickSort and a prominent computer scientist, published a research paper that described a new, and different, way of writing programmes. His Communicating Sequential Processes envisioned a system where many processes computed in parallel, communicating with each other by moving into appropriately tagged input and output states. Only when both sender and receiver were in the right states would communication occur - perhaps passing a pointer to a chunk of memory between the two.

A> As an aside, messaging appears even in Object Oriented programming, currently the dominant programming model in most spheres of software development, where data and functionality are bundled together into objects. The earliest descriptions of the idea by creators like Alan Kay referred to objects sending messages to each other to request data, or to change the state of the object. Though this was accomplished with simple function calls rather than any explicit message passing functionality, conceptually the messaging idea was central.

The main differences between the Actors and CSP were the anonymity of CSP processes, which allowed any process to match the requirements, and the implicit synchronisation at message passing. In the actor model named actors have queues of messages and can communicate purely asynchronously.

The fundamental benefits both systems shared were their inherent support for parallelism. Any process, or actor, could do all the work it wanted, and when it needed some more information or something else calculated, it could hand off work to another entity by sending a message to it, and carry on with another task.

Back in the 70s handing off to another process generally entailed switching between them on a single processor - causing some overhead. By the mid-2000s huge numbers of multi-core and multi-processor machines were available, where the ability to be doing different things on different cores was a necessity to unlock the power of the hardware. These ideas were perfect for developers building applications who wanted to take advantage of these systems. Even on a single processor machine, dealing with multiple threads of execution can be difficult when any thread can modify shared resources, such as memory. The message passing models communicate only via message channels, and modify only their own memory. This means threads communicating via messaging can safely operate without risk of colliding with each other, making for a substantially easier programming model.

Concurrent Programming

While much of this work was happening in academia, in the 80s others were beginning to review the idea of message passing processes themselves from a more practical point of view. Inside Ericsson, the Swedish telecommunications equipment provider, there was a huge pressure to build highly reliable software. Their telephone switches were at the core of many national phone networks, and for there to even be a momentary outage was unacceptable to the phone companies.

In 1986, Joe Armstrong at Ericsson hit upon the idea of separating functionality into very light weight processes that communicated via passing messages to each other - including sending a message when they died. He combined properties of the actor model and CSP by allowing applications to reach into their queue to find the messages they needed at that point, while being able to ignore other messages that had arrived in their mailbox. These two things combined to allow building trees of supervising and working processes, and simplified state machines that could handle the complex logic required for a switch.

The resulting language, Erlang, underwent heavy development, eventually powering Ericsson’s AXD301 switch. In 1998 the language was open sourced and has been subsequently used for many systems which benefit from its simple message passing model and ability to scale out to many cores or machines, and most importantly the ability to build highly reliable software. Several notable network messaging systems have themselves been implemented in Erlang, notably the XMPP (see Chapter 9) broker Ejabberd and AMQP broker RabbitMQ.

Other languages have also developed custom actor or CSP style models. In the Java world, the language Scala was used to develop Akka - a “toolkit for building highly concurrent, distributed, fault-tolerent, event driven applications on the JVM”. Akka actor processes can send messages as needed or automatically if they die, enabling Erlang-like supervision trees. In Ruby, Celluloid allows for similar work, and Google’s Go language implements Hoare’s CSP using the concept of channels to manage process interaction.

The models bring excellent process decoupling and isolation, but at the same time introduce overhead in terms of the act of copying memory for message passing, and more generally in terms of maintaining many processes. Languages such as Erlang have implemented their own scheduler so that work is switched in and out of being actively processed within a single operating system thread - or for multi-core or multi-processor machines, one thread per core.

The performance impact of message passing itself can be minimised by focusing on reducing the memory allocations. Zero copy architectures allow sending message data without copying between various intermediate memory locations (such as from user space, to kernel space, to a network buffer), which in an actor model on one machine could be just removing the pointer from one process and handing it to another. However, this can introduce an unexpected coupling between processes, so some systems intentionally enforce copying, the cost varying with the size of the data. Very small messages are another special case where often the message can be contained within the structure that is used to represent the message headers. Since many small messages are used in signalling type applications, this can be a powerful way to improve performance, and is used by some more general messaging systems to improve throughput for those cases.

The ease with which components designed with these models an be composed into large systems has influenced several principles of (wider) software design. Ideas like CQRS (Command Query Responsibility Segregation) can be applied in messaging environments, but also traditional object oriented development. CQRS suggests that messages should either request data as a response (Query), or request a change of state (Command), but not both in a single message. Other paradigms such as Flow Based Programming, and micro-service oriented architectures have tried to capitalise on the ability to think about units of computation as black boxes, with just in and out queues on which messages can be placed.

By building software with a messaging model in mind, developers also gain the power to more easily distribute that software to other nodes. As long as the constraints of remote failures are reasonably acceptable, then the distributed code need not be any different from the single machine messaging code, which is a major benefit when it comes to scaling out systems to larger and larger environments.