What is reactive programming?
Reactive programming describes a design paradigm that relies on asynchronous programming logic to handle real-time updates to otherwise static content. It provides an efficient means -- the use of automated data streams -- to handle data updates to content whenever a user makes an inquiry.
Data streams used in reactive programming are coherent, cohesive collections of digital signals created on a continual or near-continual basis. These data streams are sent from a source -- such as a motion sensor, temperature gauge or a product inventory database -- in reaction to a trigger. That trigger could be any of the following:
- An event such as software-generated alerts, keystrokes or signals from an internet of things (IoT) system.
- A call, which is a function that invokes a routine as part of a workflow.
- A message, which is an information unit that the system sends back to the user or system operator with information about the status of an operation, an error, failure or other condition.
Reactive programming creates software that responds to events rather than solicits inputs from users. An event is simply a signal that something has happened. It's generally accepted that events are "real-time" signals, meaning they're generated contemporaneously with the condition they signal, and they must be processed in real time as well. These events are best visualized as "streams" that can flow through multiple processing elements, be stopped and handled along the way, or fork and generate parallel processing activity. For the majority of cases, this processing is time-sensitive, which means that the applications require a different programming style, which is how reactive programming came about.
Reactive programming and the reactive systems it deals with consist of a combination of "observer" and "handler" functions. The former recognizes important conditions or changes and generates messages to signal they've happened, and the latter deals with those messages appropriately. The presumption in reactive programming is that there's no control over the number or timing of the events, so the software must be resilient and highly scalable to manage variable loads.
Early applications of reactive programming to business applications were largely confined to things such as monitoring the state of networks, servers or software, and signaling database conditions such as inventory levels. This focus is changing with the advent of IoT, smart buildings and cities, and public cloud computing. IoT has made the reactive model important in facilities management, industrial process control and even home automation. The cloud has introduced both a style of componentizing software -- functional computing and microservices -- and a movement to shift many reactive applications to the cloud for its scalability and reliability benefits.
How does reactive programming work?
Reactive programming is all about streams, which are time-ordered sequences of related event messages. A given stream will generally start with an observer, which can be either a segment of code inside an application that watches for some condition related to the application, or a device like an IoT sensor that generates an event. A stream is sometimes diagrammed as an arrow -- left to right -- that starts with the observer process and flows through one or more handlers until it's completely processed, terminates in an error status, or forks into derivative streams. Reactive programming is about building those observers and handlers and threading the stream as required.
Device-generated streams are easily understood. But streams generated by software-inserted observers are a bit more complicated. Normally, these elements work either in cooperation with the processing work done by an application or they run periodically to monitor a database element. When this software element recognizes a condition, it generates an event in the stream.
An event stream is steered either by the handlers themselves where work is dispatched to a specific next process, or by a message bus such as an enterprise service bus or message queue that carries the message to designated bus listeners. The message handling process determines whether a message is broadcast to multiple handlers or to a single handler, and it would also normally be responsible for load-balancing among multiple parallel handlers or providing spare handlers in the case of a failure.
Each handler must either pass the message along, determine that the stream process has ended and "eat" the message, or generate an error. The handler may decide whether to "fork" a message to multiple streams or to generate a new stream or streams. These fork conditions are often used to separate tasks in message handling; a message might generate a local response to open a gate as well as a message to a transaction processing system.
In "The Reactive Principle," the follow-up to "The Reactive Manifesto," Jonas Bonér et al. define the eight principles an application must embody to be considered reactive:
- Stay responsive. Always respond in a timely manner.
- Accept uncertainty. Build reliability despite unreliable foundations.
- Embrace failure. Expect things to go wrong and build for resilience.
- Assert autonomy. Design components that act independently and interact collaboratively.
- Tailor consistency. Individualize consistency per component to balance availability and performance.
- Decouple time. Process asynchronously to avoid coordination and waiting.
- Decouple space. Create flexibility by embracing the network.
- Handle dynamics. Continuously adapt to varying demand and resources.
Benefits and challenges of reactive programming
The primary benefits of reactive programming techniques are their ability to:
- provide better control over the response times associated with the processing of events;
- enable consistency in software design for real-time systems, to reduce development and maintenance costs and effort;
- support load balancing and resiliency to improve quality of experience; and
- make the concept of a stream or event flow explicit, improving overall management of compute elements and processing resources by making them more "visual."
These benefits come with challenges, including the following:
- Adding observer processes to current software may be difficult or impossible, depending on source code availability and staff programming skills.
- Reactive design is a major mindset shift for developers, and efforts will present a learning curve during which more validation and supervision of design and coding may be required.
- Reactive systems can easily accumulate delay through an excessive number of processes linked to the stream.
Adopting reactive programming
Good reactive programs start with a clear diagram of the event stream, one that includes all the specific handler processes and their role in processing, terminating or error generation. When this is done, the hosting platform -- edge, cloud or data center -- is selected and designated on the stream diagram for each process, avoiding any back and forth across hosting platform boundaries. Development can then begin.
The following best practices should be observed during development:
- Where an event stream must trigger a real-world response, such as opening a gate, keep the control loop short by moving the responding process closer to the front of the stream and hosting it near the event source.
- Avoid using programming languages and techniques that create stateful components that store data with the software to ensure that components can be scaled and replaced easily.
- Review the location and implementation of any databases needed by any of the handler processes to ensure that database access doesn't add latency or cross cloud boundaries, generating additional costs.
- At every step in development, reference the work done back to the event stream diagram to ensure it's maintained, up to date and accurate.
Reactive programming use cases
The primary use cases for reactive programming are the following:
- IoT applications where sensors create events that then control real-world process steps, create business transactions or both. This is the fastest-growing application of reactive programming techniques, though not the traditional target.
- Applications that gather status information from networks or data processing elements through inserted software agents that monitor activities or data elements. This is the first classic reactive programming application, but one converging with IoT.
- Any application that requires highly interactive user-to-user interface handling, especially where each keystroke must be processed and interpreted. This is the other classic reactive programming application and it now includes gaming and some social media applications.
- Signaling between applications, particularly between what could be called "foreground" applications and "background," or batch applications, that perform statistical analysis and database cleanup. This use case will normally involve a daemon process that monitors for changes and activates an event stream when one is detected.
- Coordination between functional AWS Lambda cloud processing and back-end data center processing, where an event will trigger the execution of a back-end process. This facilitates some forms of hybrid cloud development.
Looking to move your apps to an event-driven architecture? Learn how with these five design tips.