This article is featured in the DZone Guide to the Java Ecosystem. Get your free copy for more insightful articles, industry statistics, and more.
Two years ago, I collaborated with a few other developers to define a new set of architectural principles in the enterprise. It was clear at that time that everything from emergent deployment environments to user expectations to the size of datasets had outgrown previous patterns for building software. Basically what was typically “breaking” tied back to most software having synchronous call request chains and poor isolation, yielding single points of failure and too much contention.
In the Reactive Manifesto we created a new set of key principles that described the responsiveness, resilience, elasticity, and message-driven characteristics that we believed defined effective application architectures in the modern era of applications, running on everything from mobile devices to cloud-based clusters with thousands of multi-core processors.
Over the last two years, the response from the community has ranged from enthusiastic support (12,000+ signatures on the Reactive Manifesto) to eye-rolling (my favorite, in a Slashdot comment: "We want a machine that makes things cold. We don't care how it's built. We'll call this...The Refrigerator Manifesto."). Some felt that Reactive encapsulated key attributes that had long been embraced in their internal development philosophy, in the same way that some companies did Agile software development before it was “Agile.” Some felt Reactive too prescriptive, while others felt it was too generic.
So What Is Reactive?
The Reactive Manifesto’s goal is to condense the knowledge around designing highly-scalable and reliable applications into a set of four required architecture traits:
The system responds in a timely manner if at all possible. Responsiveness is the cornerstone of usability and utility, but more than that, responsiveness means that problems may be detected quickly and dealt with effectively. Responsive systems focus on providing rapid and consistent response times, establishing reliable upper bounds so they deliver a consistent quality of service. This consistent behavior in turn simplifies error handling, builds end user confidence, and encourages further interaction.
The system stays responsive in the face of failure. This applies not only to highly-available, mission critical systems—any system that is not resilient will be unresponsive during and after failure. Resilience is achieved by replication, containment, isolation, and delegation. Failures are contained within each component, isolating components from each other and thereby ensuring that parts of the system can fail and recover without compromising the system as a whole. Recovery of each component is delegated to another (external) component and high-availability is ensured by replication where necessary. The client of a component is not burdened with handling its failures.
The system stays responsive under varying workload. Reactive Systems can react to changes in the input rate by increasing or decreasing the resources allocated to service these inputs. This implies designs that have no contention points or central bottlenecks, resulting in the ability to shard or replicate components and distribute inputs among them. Reactive Systems support predictive, as well as Reactive, scaling algorithms by providing relevant live performance measures. They achieve elasticity in a cost-effective way on commodity hardware and software platforms.
As the foundation to the three traits above, Reactive Systems rely on asynchronous message-passing to establish a boundary between components that ensures loose coupling, isolation, location transparency, and provides the means to reify and delegate failures as messages. Employing explicit message-passing enables load management, elasticity, and flow control by shaping and monitoring the message queues in the system and applying back-pressure when necessary. Location transparent messaging as a means of communication makes it possible for the management of failure to work with the same constructs and semantics across a cluster or within a single host. Non-blocking communication allows recipients to only consume resources while active, leading to more efficient system utilization.
New Patterns Driving Reactive Innovation
Over the last couple of years, I believe the three most interesting new trends driving Reactive innovation are:
In traditional Java EE apps, services are written in a very monolithic way. That ties back to a strong coupling between the components in the service and between services. App servers (WebLogic, JBoss, Tomcat, etc.) are encouraging this monolithic model. They assume that you are bundling your service JARs into an EAR file as a way of grouping your services, which you then deploy—alongside all your other applications and services—into the single running instance of the app server, which manages the service “isolation” through class loader tricks; a very fragile model.
Today we have a much more evolved foundation for isolation—from the ground up—starting with, for example, Docker containers, isolated all the way up through better hardware and communication protocols. I’m excited about the Microservices momentum because it makes isolation first class, which is a necessity for resilience. You can’t build a Reactive system without isolating failures and having a separate context outside the failed component to react to the failure. You need isolation in order to avoid cascading failures.
Fast Data—the World Is Going Streaming
The whole movement towards Fast Data and real-time data requires closed feedback loops for getting data into and out of the system. The benefit of Fast Data is that you get systems that are more responsive and adaptive, allowing you to feed the results of real-time data processing back into the running system, which allows it to react to change. This capability can also be used to make these systems more resilient and scalable, but with reduced complexity.
One of the early pitfalls for streaming data in Fast Data scenarios, for example, was the lack of back-pressure. If a processing stage produced data faster than the next stage could consume that data, it would lead to a failure in the consumer, which would cause cascading failures throughout the entire processing pipeline. Reactive Streams address that problem by bringing back-pressure to streams to control the rate of data consumption. In general, the back-end systems that scale Big Data and IoT are a perfect fit for Reactive architectures.
Event Logging as the Service of Record
Another area in which I’m seeing a lot of Reactive innovation, specifically on the JVM, is when event logging is being used as the “Service of Record.” In event logging, each state change to the application is materialized as an event in the log. What you get is a database with the full history of the application; a database of facts, rather than the traditional SQL database approach that only works with a "cache of the subset of the log," as Pat Helland aptly put it. If your durable state is based on an event log, it can be easily replicated and replayed somewhere else to bring the system or component up to speed wherever it is. This is a great pattern for failure handling in distributed stream processing—if one thing fails it can be brought back up to speed and continue. Architectural patterns making use of the event log include Event Sourcing and CQRS. This way of thinking about durable state works very well with Microservices, where each service can have its own isolated, strongly consistent, event log-based, durable storage that relies on eventual consistency between the services for scale and availability. Fast Data is the foundation for this durable state and streaming architecture.
Reactive Evolving from Principles to Implementation Patterns
I believe that Reactive is on a similar arc to the one the Agile methodology followed. A lot of companies were using Agile methodologies without calling it Agile, years before the term was coined. But calling it Agile made it easier to talk about and communicate the philosophy, so the Agile Manifesto was created. When this set of principles became more familiar in the developer community, you started to see the adoption of Scrum for distilling the essence of the processes and XP for supporting programming principles and practices like test-driven development, pair programming, and continuous integration.
Today we’re seeing the same shift for Reactive. The core message of Reactive is aimed at core principles rather than tools and techniques. But Microservices, Fast Data, and Event Logging are great examples of how implementation patterns within the Reactive movement are starting to get more definition and momentum.
For more insights on microservices, JVM languages, and more trends in Java, get your free copy of the DZone Guide to the Java Ecosystem!