Introduction to reactive
Reactive is ideal for low latency, high throughput workloads.
Reactive processing is a paradigm (specification) that enables developers to build non blocking, asynchronous applications that can handle back pressure (flow control)
Reactive streams provides a standard for asynchronous stream processing without blocking back pressure.
Reactor is the fourth generation response Library Based on the reactive streams specification, which is used to build non blocking applications on the JVM.
Project reactor is a completely non blocking foundation, including back pressure support. It is the foundation of the responsive stack in the spring ecosystem, and has its presence in projects such as spring Webflux, spring data and spring cloud gateway. Project reactor can be used to design an efficient and responsive system. Just now, reactive streams is the specification, so project reactor is the implementation.
2. Responsive programming
Responsive programming is an asynchronous programming style that focuses on data flow and the propagation of changes.
Responsive programming is a declarative programming paradigm related to data flow and change propagation. Using this example, you can easily represent static (for example, arrays) or dynamic (for example, event emitters) data streams, and you can also represent inferred dependencies in the association execution model, which helps to change the automatic propagation of data streams.
Reactive programming
Imperative programming
In imperative programming, a: = B + C means that the result of B + C is assigned to a, and the change of the value of B or C will not affect the value of A. In reactive programming, the value of a will be automatically updated with the change of B or C, and there is no need to re execute a: = B + C to determine the value currently assigned to a. (PS: is it similar to MVVM frameworks such as angularjs and vuejs? The view is bound to the model. When the model changes, the view automatically changes)
For example, in the model – view – controller (MVC) architecture, responsive programming can facilitate changes in the underlying model, which are automatically reflected in the associated views.
There are many similarities between responsive programming and the observer pattern commonly used in object-oriented programming.
From a push-pull perspective, responsive programming is "push", which actively pushes changes to its subscribers. Publisher subscriber are two very important concepts.
Imagine that the data flow starts from the source, goes through node by node, and finally reaches the destination. The node is equivalent to an operator. After processing, the stream is emitted and re emitted to the next node.
I always think this process is familiar, much like the processing method of Apache storm. In a topology, the data flow is sent out from the sput, processed by several bolts, and finally collected to a certain place.
There is another understanding, which I think is also very good. It says that reactive programming is a programming model that constructs transaction relationships through asynchrony and data flow. Things can be understood as a processing process and an execution process. Responsive programming is to build relationships, transactions and relationships between transactions. The data flow is like a bridge. The data flow flows from one transaction to the next.
Imagine that the Yangtze River flows through Yibin, Luzhou, Chongqing, Fuling, Wanzhou, Yichang, Jingzhou, Wuhan, Huangshi, Ezhou, Jiujiang, Anqing, Tongling, Wuhu, Nanjing and Shanghai, and finally into the East China Sea.
Just as completefuture arranges the future.
In essence, responsive programming is a response to data flow or some change, but when this change occurs is unknown, so it is an asynchronous and callback based way to deal with problems
3. NIO
NIO(Non-Blocking I/O)
BIO(Blocking I/O)
In the classic thread model, socket accept()、socket. read()、socket. The three main functions of write () are synchronously blocked. When a connection is processing I / O, the system is blocked. If a single thread is used, it will be blocked there, but the CPU is not blocked. If multiple threads are used, the CPU can handle more things. In fact, this is also the essence of using multithreading: when I / O blocks the system, but the CPU is idle, multithreading can be used to use CPU resources. However, the cost of thread creation, destruction and switching is very high.
In fact, all system I / O is divided into two phases: waiting for ready and operation. For example, the read function is divided into waiting for the system to read and real reading; Similarly, the write function is divided into waiting network card can write and real write.
It should be noted that the blocking waiting for ready does not use CPU and is "empty"; The real blocking of read and write operations uses the CPU and is really "working".
With socket Read() is an example:
Socket in traditional bio Read(), if there is no data in TCP recvbuffer, the function will block until the data is received and return the read data.
For NiO, if the TCP recvbuffer has data, the data is read from the network card to the memory and returned to the user; Otherwise, it returns 0 directly and will never block.
In the bio model, there is no way to know whether you can write or read. You can only be "stupid". In the NiO model, if a connection cannot be read or written (socket. Read() returns 0 or socket Write () returns 0). We can write this down. The way of recording is usually to register the flag bit on the selector, and then switch to other ready channels to continue reading and writing.
NiO's main events are: read ready, write ready, and new connections coming. Then, first register the corresponding processor when these events arrive, and then tell the event selector at the right time: I am interested in this event, and finally use an endless loop to select the ready event. Select is blocked, so you can safely and boldly call this function in a while (true) without worrying about CPU idling.
To sum up, register all interested event processors, single thread polling, select ready events, and execute event processors.
We can roughly summarize how NiO solves the bottleneck of threads and handles massive connections:
NiO has changed from blocking read-write (occupying threads) to single thread polling events. Find network descriptors that can be read and written for reading and writing. Except that the polling of events is blocked (there is nothing to do but to be blocked), the remaining I / O operations are pure CPU operations, and it is not necessary to start multithreading.
NiO has changed from blocking read-write (occupying threads) to single thread polling events. Find the network descriptor that can be read and written for reading and writing. Except that the polling of events is blocked (if there is nothing to do, it must be blocked). The remaining I / O operations are pure CPU operations, and there is no need to start multithreading. Moreover, due to the saving of threads, the problems caused by thread switching are also solved when the number of connections is large, which provides the possibility to handle a large number of connections. The efficiency of single thread processing I / O is indeed very high. There is no thread switching, but just desperately reading Write and select events. However, today's servers are generally multi-core processors. If we can use multi-core for I / O, it will undoubtedly improve the efficiency.
Buffer (buffer)
In NiO, all data is processed with buffers. When reading data, it is directly read into the buffer; When writing data, it is also written to the buffer.
Channel
A channel is an object through which data can be read and written. Of course, all data is processed through the buffer object. We never write bytes directly to the channel. Instead, we write data to a buffer containing one or more bytes. Similarly, instead of directly reading bytes from the channel, the data is read from the channel into the buffer, and then the byte is obtained from the buffer.
Selector
The selector class is the core class of NiO, Selector (selector) the selector provides the ability to select ready tasks. The selector will continuously poll all channels registered on it. If a channel is ready for events such as reading and writing, it will be in the ready state. The selector can continuously poll to find ready channels for subsequent IO operations. A selector can poll multiple channels at the same time. In this way, a single thread can manage multiple channels and thus multiple network connections. In this way, there is no need to create a thread for each connection, and the overhead caused by context switching between multiple threads is avoided.
A simple example of reading a file:
Server. java
Client. java
About the usage of selector
reference resources:
https://spring.io/reactive
https://www.jianshu.com/p/d47835316016
https://www.cnblogs.com/haimishasha/p/10756448.html
https://tech.meituan.com/2016/11/04/nio.html
Recommended Java NiO tutorial for wall crack
http://tutorials.jenkov.com/java-nio/index.html
http://tutorials.jenkov.com/java-nio/selectors.html
http://tutorials.jenkov.com/java-nio/server-socket-channel.html