In this video, see the different elements involved in reactive programming, the principles of it, and how you can use it in Java.
- [Instructor] Hi, welcome to the fifth video of this section, Reactive Explained in a Simple Way. In the previous video, we saw the benefits of Reactive programming and how we could convert our old logic to a Reactive one. From the first video, we know some facts of Reactive programming and the basics of it. In this video, we are going to talk more in detail about it. What elements are involved? What is backpressure and why we need that. We'll also see the principles of Reactive applications which sets the fundamentals of it.
This is great, but the courses have Java. We'll see what options we have to support Reactive programming in Java. Let's go back in time to the first video of this section. We said that Reactive programming is a way of programming with a asynchronous data streams or flows in which we have propagations, propagation of change involved. Let's see how it works with another analogy. As we said, a stream is a sequence of ongoing events ordered in time. How can we imagine that? Let's imagine a pipe that can transport data.
Like water, the data would move in a certain direction. In Java, because it's an object-oriented programming language, the data is going to be different objects. We'll send objects through the pipe. It's worth mentioning that the data stream or flow is of one type. You can only send updates of the same type. Now, it's time to introduce a new entity. This entity is going to be the source of information. It's going to emit data. It might create it or not, but it's going to send it.
As we can see in the image, it's going to sit at the beginning of the pipe. What kind of different data can we send? As I mentioned before, we can send objects. In this sample, we are sending a string object. In this case, the pipe is a type of string. We can also send an error signal. That would mean that something went wrong in the source of information. That's going to mean that the communication is finished and no more items are going to be sent. Another signal is the completed one. That would mean that the source of information has finished sending items.
As the error signal, this is going to close the data stream. So, we have a source of information that sends data in a data stream. We are missing another entity, right? Yeah, we need consumers. This is the entity which receives the information and potentially processes it. It's going to sit at the end of the pipe. As we mentioned in the previous video, the consumer is not going to pull the data. The data is going to be pushed to the consumer. The consumer doesn't know when the data is going to come.
Let me introduce you to another entity, the subscription. The subscription is going to manage how the data is going to flow in the stream. The consumer can start receiving data from the source of information by subscribing to the data flow. This is by means of a subscription. The consumer will receive the items that the source sends. In the previous example, the consumer will get data stream once it's sent by the source. We'll see this later on in this video, but the subscription in Java is going to deal with backpressure so that source of information cannot overflow the consumer with data.
We need to know that there are different types of relationships between sources of information and consumers. We're not going to cover this again in this course, but I want you to know that this is possible. We can have different types of sources of information, cold and hot. A cold source of information will start running upon subscription. That means that the source starts pushing values to the consumer when a subscription is in place. A hot source of information is always emitting data, even before a subscription is active.
Let's see this in more detail. The source of information is cold if its underlying producer is created and activated during subscription. This means that the sources of information are functions, for example, then the producer is created and activated by calling that function. That the steps are first, creates the source of information. Second, activates the source of information. Third, consumer starts listening to the source. And fourth, unicast. It's unicast because only the consumer is listening to information that's being sent.
An observable is hot if its underlying producer is either created or activated outside the subscription. The steps are first, the consumer shares a reference to the source of information. Second, consumer starts listening to the producer. And third, it's multicast, usually. Hot observables are usually multicast because they could be listening to a producer that only supports one listener at a time. The grounds for calling it multicast at that point are a little fuzzy. This is all you need to know about how hot and cold are sources of information.
Now, we are going to move to another topic, the principles of Reactive programming. Now, we are going to move on to another topic, the principles of Reactive programming. The Reactive Manifesto studies four different guiding principles, responsive, resilient, elastic or scalable, or message-driven. A responsive application is the goal. A responsive application is both scalable and resilient. Responsiveness is impossible to achieve without both scalability and resilience.
A message-driven architecture is the foundation of scalable, resilient, and ultimately responsive systems. Responsiveness means that problems may be detected quickly and dealt with effectively. The system responds in a timely manner if possible. This is key for usability and utility. 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 our handling, builds end-user confidence, and encourages further interaction. Any system that is not resilient will be unresponsive after a failure. Modern applications must be resilient at their core in order to stay responsive under a variety of real-world, less-than-ideal conditions. Performance, endurance, and security are all facets of resiliency. Your applications must be resilient on all levels, not just a few.
Usually, 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 higher reliability is ensured by replication where necessary. The beauty of building on top of a message-driven core is that you naturally get a number of valuable building blocks to work with. Resiliency and scalability go hand-in-hand when creating consistently responsive applications.
Their designs are supposed to be with no contention points or bottlenecks and support predictive, Reactive scaling algorithms. Should we scale up or out? There are different ways to scale an application. Scaling up involves maximizing the resources of a single CPU server, often requiring the purchase of hardware. Scaling out involves distributing computation across a cluster of cheap commodity hardware, which is cost-efficient but very difficult to accomplish when your system is based around the concepts of time and space.
As we mentioned earlier, I mentioned streaming architecture provides the asynchronous boundary needed to decouple from time and space, providing the ability to easily scale out on demand, also known as elasticity. Reactive applications are difficult to build with thread-based frameworks because of how difficult it is to scale out an application based on shared mutable state, threads, and locks. Shared mutable state also makes it difficult, though not impossible, to scale up. Reactive systems rely on asynchronous message-passing to establish a boundary between components that ensures loose coupling, isolation, and location transparency.
One of the benefits is that you can delegate failures as messages. Non-blocking communication allows recipients to only consume resources while active, leading to less system overhead. A message-driven application may be event-driven, actor-based, or a combination of the two. An event-driven system is based on events which are monitored by zero or more observers. Actor-based concurrency is an extension of the message-passing architecture, where messages are directed to a recipient which happens to be an actor.
The main difference between messages and events is that messages are directed, while events just happen. Messages have a clear destination, while events might be observed by zero or more observers. These are the four principles of Reactive programming. They make sense, right? We can understand much better now how it gets all those benefits. We taught this in video two. By its nature, they are just there. Let's change topic now. We're going to talk a little bit about backpressure.
Backpressure provides resilience. How? It ensures that all participants participate in flow control. With a push model, our system can become flooded if the source of information flows with full force. Push, of course, works well when consumers are faster than the source of information. However, it's fairly difficult to predict from one moment to the next one whether the source will be faster than the consumer over a period of time. Unfortunately, most stream processes and systems force us to choose between push or pull at development time.
How can we solve this? The first solution is to pull. This protects us from overwhelming our slower consumers. It also wastes system resources when consumers are faster than the source of information, because it prevents a faster source from running at full speed when consumers have enough capacity to keep up. Another option is to increase the buffer size of consumers. This is possible but not always realistic. Having unlimited memory would be nice, but we'd have to increase the buffer size of cues throughout our entire system.
Another option is to simply drop elements. Working in stream processing gives us some flexibility for dropping what can't be handled, but it's not always appropriate. What we need is a bi-directional flow of data, elements emitting downstream from source of information to our consumer, and a signal for demanding emitted upstream from consumer to source. This also prevents wasting resources, because demand is signaled asynchronously. Our source can send many requests for more work before actually receiving any.
Providing backpressure all the way through the entire flow gives the system a chance to respond to excessive flow. This is enough about backpressure now. We'll see that this topic in the second volume when talking about how to handle it with RxJava 2.0. Let's change topic again and see the different ways of how Java can support Reactive programming. Let's change topic again and see the different ways of how Java can support Reactive programming. Java is not a Reactive language in the sense that it doesn't support it natively until Version 9, but we can have Reactive layers on top of the JDK.
We are only going to take a very brief look at a few of them here. Reactive Streams is not a library. It's a very low-level contract like a standard expressed as a handful of Java interfaces, but those are applicable to other languages. It's a standard for asynchronous stream processing with non-blocking backpressure. RxJava is a library made by Netflix. They were using Reactive patterns internally for some time, and then they released the tools they were using under an open source license.
Reactor is a Java framework from the pivotal open source team. It builds directly on Reactive Streams, so there is no need for a bridge. A Spring Framework 5.0 has Reactive features built into it, including those for building HTTP servers and clients. Ratpack is a set of libraries for building high-performance services over HTTP. It builds on Netty and implements Reactive Streams for interoperability. Akka is a toolkit for building applications using the actor pattern in Scala or Java with interprocess communication using Akka Streams.
Also, Reactive Streams contract are built in. As you can see, there are multiple options, but for now, we are going to focus on Reactive Streams, which is going to be the topic of the next video. We have interest for more time talking about RxJava 2.0 and Akka. I admit this has been an intense video, a lot to take on. The different entities in Reactive programming, the four principles, backpressure, and the options we have in Java nowadays. The next video is going to be lighter in content. We are going to talk about Reactive Streams, which I briefly introduced before.
See you there.
This course was created and produced by Packt Publishing. We are honored to host this training in our library.
- What is reactive programming?
- Java 9 reactive features
- Creating and observing sources with RxJava
- Unit testing
- Akka streams in a reactive environment
- Building a sample reactive application