Modern applications are becoming more and more demanding in terms of performance and response rate. To respond to new requirements, unusual paradigms are becoming helpful. In this topic, we are going to talk about one of the most important paradigms: reactive programming.
What is reactive programming
Reactive programming is a paradigm based on the idea of handling events. By an event we mean something happening in the system in real time. It could be air temperature, bank account balance updates, email messages, notifications on mobile phones, a user clicking on a website, HTTP requests, and so on. A sequence of events forms a data stream. Reactive programming allows observing data streams and reacting once a value is emitted.
To react to changes a programmer declares how to handle incoming events. The code will be executed later, once an event is emitted. Such code is also called asynchronous.
Let's consider a couple of cases when reactive programming is useful.
The traditional approach
Let's consider a web application with 2 components communicating via HTTP protocol. The traditional approach to handling requests supposes reserving a thread per incoming request. For instance, the web server has a dedicated thread pool that handles HTTP requests. Once a request comes, an available thread from a thread pool is taken to handle the request and used until the request's completion.
Imagine Component A gets a request from a user, handles it, and sends another request to Component B. The component's thread is blocked and waits for the completion of the request by Component B. Moreover, this thread can't be reused by other requests — it is merely useless during this wait time.
Requesting another component is not a single example of thread being idle. A thread could do a request to a database or just be blocked by I/O operations or heavy calculations. There is another limitation of the traditional approach. If the request rate is high, a significant number of threads is needed. Otherwise, the queue of incoming requests will only grow. It means the Java process will consume a lot of memory.
Reactive solution
There is a way to avoid overheads related to thread blocking: reactive programming. It relies on asynchronous logic that provides non-blocking interactions whenever possible. Non-blocking operations are also called asynchronous operations.
Let's return to the previous example and show what it will look like in the case of the reactive programming approach. Component A sends an asynchronous request to Component B. It means Component B will respond immediately but won't return the operation's result. That means the thread of Component A will not be blocked and could be reused while Component B is processing the request. Once Component B completes the request, it sends the operation result back to Component A.
In this case, a data stream has a single event in terms of reactive programming. Component A observes the data stream and has some logic to process the event. Once Component B completes its part, it pushes the event to the data stream. Finally, Component A is notified about the event and finishes processing asynchronously.
Publisher-subscriber pattern
Let's look at another case when reactive programming makes an application's architecture more clear and effective. Suppose there is a weather station that checks the air temperature every 5 minutes. The station exposes API to external clients to provide results of measurements to the public. It could be a website that shows the current weather. Such a website has a specific thread that requests API every 5 minutes. This is the traditional way:
In the reactive programming approach, each client subscribes to changes in air temperature. Once the value is changed, clients are notified of it. It helps to avoid disturbing the weather station every 5 minutes, even when the temperature is still the same. At the same time, clients are getting updated in case of changes in air temperature. Such a pattern is especially valuable in the case of many clients. The same subscriber could subscribe to many publishers as well.
Reactive streams standardization
There are several libraries in Java that implement the reactive programming paradigm. The most popular ones are RxJava, Project Reactor, and Java standard API introduced in Java 9. Although these libraries were created by different authors, they have a lot in common. What is more, all of them follow the reactive streams initiative that tries to standardize reactive programming paradigm implementation by introducing API.
The reactive streams initiative declares 3 basic interfaces: Publisher, Subscriber, and Subscription. Let's look at them based on the weather station example. To stay tuned, Subscriber subscribes to Publisher by invoking its subscribe method. To manage data streams, Subscriber uses Subscription, which makes it possible to suspend or request data. Once data is no longer needed, the subscription could be canceled.
Subscriber has 4 methods. onSubscribe customizes a method invoked after the subscription to a publisher. Probably the most popular method is onNext. It is triggered on each incoming event and serves to handle it. In our example, a client takes an incoming value and updates the air temperature on its website. onError method is used to handle errors. If Publishercan't provide the correct value for some reason, it may cause an error. Once data stream handling is completed, onComplete method is invoked.
Conclusion
Reactive programming is a paradigm based on data streams. It is becoming more popular with the growing requirements for modern software applications. In many cases, it helps to save resources and makes the architecture clearer. There are many implementations of reactive programming in Java. The most popular are RxJava and Project Reactor. Both of them are based on API provided by Reactive streams — an initiative for implementation standardization. It has 3 basic interfaces: Publisher, Subscriber, and Subscription.