What Is Stream Output?

1. Interpretation

1.1 Stream

Stream, also known as Reactive, is an R&D framework based on asynchronous data streams and a concept and programming model rather than a technical architecture. Today, responsive technical frameworks are commonly used in technical stacks, such as React.js and RxJs at the frontend and RxJava and Reactor at the server, with RXJava in the Android system. All of these created reactive programming.

1.2 Reactive Programming

Reactive Programming is a programming paradigm based on an event model. There are two ways to obtain the last task execution result in asynchronous programming; one is proactive polling, which is called Proactive mode, and the other is passive feedback receiving, which is called Reactive mode. In short, in Reactive mode, the feedback of the previous task is an event that triggers the execution of the next task.

1.3 Stream Output

The Stream output concept was introduced in an internal closing meeting after a performance competition. It is based on the stream processing theory applied in page rendering and the rendered HTML in network transmission. Therefore, it is also called stream rendering. To be more specific, the page is split into several independent modules, with separated data sources and page templates in each module. Server-side stream operations are performed on each module for business logic processing and page template rendering. Then, the rendered HTML is output to the network in-stream. After that, the chunked HTML data is transmitted in-network, and the streaming chunked HTML data is rendered and displayed one by one in the browser. The detailed procedure is shown on the diagram below:

1.4 End-to-End Reactive

The data stream output on the network works for the data between the client and the web server and various microservice servers, as shown in the following figure:

2. Theoretical Foundation of Stream Output

The following explains several core technologies, including the basic technical theory that supports the data stream output and receiving shown above.

2.1 HTTP Chunked Transfer Protocol

Chunked transfer encoding is a data transmission mechanism of HTTP. In this mechanism, HTTP data sent by a web page server to a client application (usually a web browser) can be divided into multiple parts. Chunked transfer encoding is only available in HTTP/1.1.

HTTP/1.1 200 OK\r\n
\r\n
Transfer-Encoding: chunked\r\n
...\r\n
\r\n
<chunked 1 length>\r\n
<chunked 1 content>\r\n
<chunked 2 length>\r\n
<chunked 2 content>\r\n
...\r\n
0\r\n
\r\n
\r\n
func handleChunkedHttpResp(conn net.Conn) {
buffer := make([]byte, 1024)
n, err := conn.Read(buffer)
if err != nil {
log.Fatalln(err)
}
fmt.Println(n, string(buffer))
conn.Write([]byte("HTTP/1.1 200 OK\r\n"))
conn.Write([]byte("Transfer-Encoding: chunked\r\n"))
conn.Write([]byte("\r\n"))
conn.Write([]byte("6\r\n"))
conn.Write([]byte("hello,\r\n"))
conn.Write([]byte("8\r\n"))
conn.Write([]byte("chunked!\r\n"))
conn.Write([]byte("0\r\n"))
conn.Write([]byte("\r\n"))
}

2.2 HTTP SSE Protocol

Server Send Events (SSE) is a standard HTTP protocol. It is used when a server sends events to a client in stream. The monitoring functions are bound to some event types in clients to process business logic. SEE is only used in one-way. Only the server can send events to the client in stream. The specific process is shown on the diagram below:

// Client initialization event source
const evtSource = new EventSource("//api.example.com/ssedemo.php", { withCredentials: true } );
// Add a processing function for message events to monitore messages sent from the server
evtSource.onmessage = function(event) {
const newElement = document.createElement("li");
const eventList = document.getElementById("list");
newElement.innerHTML = "message: " + event.data;
eventList.appendChild(newElement);
}
date_default_timezone_set("America/New_York");
header("Cache-Control: no-cache");
header("Content-Type: text/event-stream");
$counter = rand(1, 10);
while (true) {
// Every second, send a "ping" event.
echo "event: ping\n";
$curDate = date(DATE_ISO8601);
echo 'data: {"time": "' . $curDate . '"}';
echo "\n\n";
// Send a simple message at random intervals.
$counter--;
if (!$counter) {
echo 'data: This is a message at time ' . $curDate . "\n\n";
$counter = rand(1, 10);
}
ob_end_flush();
flush();
sleep(1);
}
event: userconnect
data: {"username": "bobby", "time": "02:33:48"}
event: usermessage
data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}
event: userdisconnect
data: {"username": "bobby", "time": "02:34:23"}
event: usermessage
data: {"username": "sean", "time": "02:34:36", "text": "Bye, bobby."}

2.3 WebSocket

  • WebSocket is a full-duplex communication protocol different from HTTP. It is relatively intricate and features a higher degree of code intrusion.
  • SSE is a standard HTTP protocol that supports half-duplex communication. It supports reconnection as well as event and data type customization. It is relatively more portable and flexible.

2.4 RSocket

In the microservices architecture, data is transmitted between different services through application protocols. Typical transfer methods include the REST or SOAP API based on HTTP protocol and RPC based on TCP byte stream. However, the request-response mode is only supported for HTTP. Therefore, the client must use polling to obtain the latest push messages, which undoubtedly causes resource waste. Furthermore, if the response time of a request is too long, the processing of subsequent requests will be blocked. Although SSE can be used to push messages, it is only a simple text protocol with limited functions. By comparison, WebSocket supports two-way data transfer with no application-layer protocols supported. Therefore, RSocket solves the various problems of the existing protocols.

import io.rsocket.AbstractRSocket;
import io.rsocket.Payload;
import io.rsocket.RSocket;
import io.rsocket.RSocketFactory;
import io.rsocket.transport.netty.client.TcpClientTransport;
import io.rsocket.transport.netty.server.TcpServerTransport;
import io.rsocket.util.DefaultPayload;
import reactor.core.publisher.Mono;
public class RequestResponseExample { public static void main(String[] args) {
RSocketFactory.receive()
.acceptor(((setup, sendingSocket) -> Mono.just(
new AbstractRSocket() {
@Override
public Mono<Payload> requestResponse(Payload payload) {
return Mono.just(DefaultPayload.create("ECHO >> " + payload.getDataUtf8()));
}
}
)))
.transport(TcpServerTransport.create("localhost", 7000)) // Specify the transport layer implementation
.start() // Start the server
.subscribe();
RSocket socket = RSocketFactory.connect()
.transport(TcpClientTransport.create("localhost", 7000)) // Specify the transport layer implementation
.start() // Start the client
.block();
socket.requestResponse(DefaultPayload.create("hello"))
.map(Payload::getDataUtf8)
.doOnNext(System.out::println)
.block();
socket.dispose();
}
}
Reactive Programming Framework
@Override
public Single<Integer> remaining() {
return Flowable.fromIterable(LotteryEnum.EFFECTIVE_LOTTERY_TYPE_LIST)
.flatMap(lotteryType -> tairMCReactive.get(generateLotteryKey(lotteryType)))
.filter(Result::isSuccess)
.filter(result -> !ResultCode.DATANOTEXSITS.equals(result.getRc()))
.map(result -> (Integer) result.getValue().getValue())
.reduce((acc, lotteryRemaining) -> acc + lotteryRemaining)
.toSingle(0);
}

3. Stream Output Scenarios

Performance, experience, and data are the three major things in the daily work of developers. Performance has always been the core point of developers. Stream output is created for performance issues. The suitable stream output scenarios are shown on the diagram below:

3.1 Page Stream Output Scenario

Compared with a static page, a dynamic page is mainly composed of the page style, JavaScript of page interaction, and the dynamic page data. Apart from the time consumed in different stages of the lifecycle above, this scenario also contains the page rendering. When the browser receives HTML, it will perform DOM tree construction, scanner preloading, CSSOM tree construction, Javascript compilation and execution, in-process CSS file loading, and JS file loading to block page rendering. If we split the page and streaming data in the following way, the performance will improve significantly.

<!-- Module 1 -->
<html>
<head>
<meta />
<link />
<style></style>
<script src=""></script>
</head>
<body>
<!-- Module 2 -->
<div>xxx</div>
<div>yyy</div>
<div>zzz</div>
</body>
</html>
  • Time to First Byte (TTFB)
  • Time for the packet arriving at the browser and downloading HTML
  • Time for CSS and JS loading and execution
  • Time for network transmission after modules are split
<!-- Module 1 -->
<html>
<head>
<meta />
<link />
<style></style>
<script src=""></script>
</head>
<body>
<!-- Module 2 -->
<div>xxx1</div>
<div>yyy1</div>
<div>zzz1</div>
<!-- Module 3 -->
<div>xxx2</div>
<div>yyy2</div>
<div>zzz2</div>
<!-- Module 4 -->
<div>xxx3</div>
<div>yyy3</div>
<div>zzz3</div>
</body>
</html>

3.2 Data Stream Output Scenarios

Single-Interface Big Data

  • Time to First Byte (TTFB)
  • Time for the packet arriving at the client and downloading data
  • Time for data transmission in the network

4. Summary

  • Stream output used to be stream rendering and has become reactive from end-to-end. Despite the improvements in performance experience, you should consider the acceptance of R&D model changes and the increased O&M costs.
  • Several technical solutions suitable for different service scenarios for stream output were briefly introduced.
  • Several scenarios suitable for stream output, together with page and data splitting were proposed.

References

[1] https://rsocket.io/

Original Source:

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Alibaba Cloud

Alibaba Cloud

Follow me to keep abreast with the latest technology news, industry insights, and developer trends. Alibaba Cloud website:https://www.alibabacloud.com