The reactive web core

Let's imagine that we are working on the new asynchronous non-blocking web module for the new Spring ecosystem. How should the new reactive web stack look? First of all, let's analyze the existing solutions and highlight the parts that should be enhanced or eliminated.

It should be noted that, in general, the internal API of Spring MVC is well-designed. The only thing that should be added to the API is a direct dependency on the Servlet API. Therefore, the final solution should have similar interfaces as the Servlet API. The first step toward designing the reactive stack is replacing javax.servlet.Servlet#service with an analogical interface and a method that reacts to incoming requests. We also have to change the related interfaces and classes. The Servlet API's way of exchanging a client's request for a server's response should be enhanced and customized as well.

Although the introduction of our own API gives us decoupling from the server engines and concrete APIs, it does not help us set up reactive communication. Hence, all new interfaces should provide access to all data, such as the request body and the session in the reactive format. As we learned from the previous chapters, the Reactive Streams model allows to interact with and process data according to its availability and demand. Since Project Reactor follows the Reactive Streams standard and provides an extensive API from the perspective of functionality, it could be an appropriate tool for building all reactive web APIs on top of it.

Finally, if we combine these things in a real implementation, we come up with the following code:

interface ServerHttpRequest {                                      // (1) 
... //
Flux<DataBuffer> getBody(); // (1.1)
... //
} //

interface ServerHttpResponse { // (2)
... //
Mono<Void> writeWith(Publisher<? extends DataBuffer> body); // (2.1)
... //
} //

interface ServerWebExchange { // (3)
... //
ServerHttpRequest getRequest(); // (3.1)
ServerHttpResponse getResponse(); // (3.2)
... //
Mono<WebSession> getSession(); // (3.3)
... //
} //

The preceding code can be explained as follows:

  1. This is the draft of the interface that represents an incoming message. As we can see, at point (1.1), the central abstraction that gives access to the incoming bytes is Flux, which means by definition that it has reactive access. As we may remember from Chapter 5Going Reactive with Spring Boot 2, there is a helpful abstraction over the bytes' buffer, which is DataBuffer. This is a convenient way of exchanging data with a particular server implementation. Along with the request's body, any HTTP request usually contains information about incoming headers, paths, cookies, and query parameters, so that information may be expressed as separate methods in either that interface or its sub-interfaces.
  2. This is the draft of the response interface, which is a companion interface to the ServerHttpRequest interface. As shown at point (2.1), unlike the ServerHttpRequest#getBody method, the ServerHttpResponse#writeWith method accepts any Publisher<? extends DataBuffer> class. In that case, the Publisher reactive type gives us more flexibility and decouples from that particular reactive library. We may, therefore, use any implementation of the interface and decouple the business logic from the framework. The method returns Mono<Void>, which represents an asynchronous process of sending data to the network. The important point here is that the process of sending data is executed only when we subscribe to the given Mono. Furthermore, the receiving server may control the backpressure, depending on the transport protocol's control flow.
  3. This is the ServerWebExchange interface declaration. Here, the interface acts as a container for the HTTP request-response instances (at 3.1 and 3.2). The interface is infrastructural and, as well as the HTTP interaction, may hold information related to the framework. For example, it may contain information about a restored WebSession from the incoming request, as shown at point (3.3). Alternatively, it may provide additional infrastructural methods on top of the request and response interfaces.

In the preceding example, we drafted the potential interfaces for our reactive web stack. In general, those three interfaces are similar to those we have in the Servlet API. For example, ServerHttpRequest and ServerHttpResponse may remind us of ServletRequest and ServletResponse. Essentially, reactive counterparts are aimed at providing almost identical methods from the perspective of the interaction model. However, due to the asynchronous and non-blocking nature of Reactive Streams, we have an out-of-the-box streaming foundation and protection from intricate callback-based APIs. This also protects us from callback hell.

Aside from the central interfaces, to fulfill the whole interaction flow, we have to define the request-response handler and filter API, which may look as follows:

interface WebHandler {                                             // (1)
Mono<Void> handle(ServerWebExchange exchange); //
} //

interface WebFilterChain { // (2)
Mono<Void> filter(ServerWebExchange exchange); //
} //

interface WebFilter { // (3)
Mono<Void> filter(ServerWebExchange exch, WebFilterChain chain);//
} //

The numbered sections in the preceding code can be described as follows:

  1. This is the central entry point for any HTTP interaction, called WebHandler.  Here, our interface plays the role of an abstract DispatcherServlet, so we can build any implementation on top of it. Since the responsibility of the interface is to find a request's handler and then compose it with a view's renderer that writes the execution's result to ServerHttpResponse, the DispatcheServlet#handle method does not have to return any results. However, it might be useful to be notified upon completion of the processing. By relying on a notification signal like this, we may apply a processing timeout so that, if no signal appears within a specified duration, we may cancel execution. For this reason, the method returns Mono from Void, which allows to wait for the completion of the asynchronous processing without necessarily handling the result. 
  2. This is the interface that allows to connect a few WebFilter instances (3) into a chain, similar to the Servlet API.
  3. This is the reactive Filter representation.

The preceding interfaces give us a foundation on top of which we may start to build the business logic for the rest of the framework.

We have almost completed the essential elements of the reactive web infrastructure. To finish the abstraction hierarchy, our design requires the lowest-level contract for reactive HTTP request handling. Since we previously defined only the interfaces responsible for data transfer and processing, we have to define an interface that takes responsibility for adapting a server engine to the defined infrastructure. For that purpose, we need an additional level of abstraction that will be responsible for direct interaction with ServerHttpRequest and ServerHttpResponse.

Moreover, this layer should be responsible for building ServerWebExchange. The particular Session storage, the Locale resolvers, and a similar infrastructure are held here:

public interface HttpHandler {
    Mono<Void> handle(
ServerHttpRequest request,
ServerHttpResponse response); }

Finally, for each server engine, we may have an adaption that calls the middleware's HttpHandler, which then composes the given ServerHttpResponse and ServerHttpRequest to ServerWebExchange and passes it to WebFilterChain and WebHandler. With such a design, it does not matter to Spring WebFlux users how the particular server engine works, since we now have a proper level of abstraction that hides the details of a server engine. We can now move on to the next step and build a high-level reactive abstraction.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.190.156.93