基础概念
HTTP 流简单的理解为:服务端维持一个 HTTP 连接,通过这个 HTTP 连接源源不断、持续的输出内容至客户端。相较于我们常见的 HTTP 请求(一次返回),流处理的特点在于持续返回。
我们举个简单的应用场景:股票网站的股价更新。你可以使用轮询的方式,前端设置定时器周期性的请求股价接口来刷新股价。除此之外,可以使用 HTTP Streaming 的方式,只需要维持一个 HTTP 链接,就可以由后端自行将最新的股价信息 Push 到客户端来完成实时刷新。
同时需要注意,为了实现这种持续返回的效果,服务端需要在客户端返回的 Header 中设置 Transfer Encoding: chunked
[2]。
To achieve an indefinite response, the server must respond to client requests by specifying Transfer Encoding: chunked in the header. This sets up a persistent connection from server to client and allows the server to send response data in chunks of newline-delimited strings. These chunks of data can then be received and processed on-the-fly by the client.
服务端初体验
我们使用 php fpm + nginx,来完成服务端的 http 流输出。PHP 代码如下
1 | if (ob_get_level() == 0) ob_start(); |
我们看到其中有 str_pad('',4096)
,为填充输出缓冲区,进而保障每次 flush 都有数据能输出到客户端。因为我们的服务经过 Nginx,而 Nginx 的 proxy_buffer_size [3] 默认是 4k,所以我们需要填充完缓冲区后,才能保证每次的 Server 端输出可以直接打到客户端。(当然了,你也可以关闭 Nginx 的缓冲区设置)
其输出为1
2
3
4
5
6
7
8
9
10Line to show. At2021-04-11 01:54:52
Line to show. At2021-04-11 01:54:54
Line to show. At2021-04-11 01:54:56
Line to show. At2021-04-11 01:54:58
Line to show. At2021-04-11 01:55:00
Line to show. At2021-04-11 01:55:02
Line to show. At2021-04-11 01:55:04
Line to show. At2021-04-11 01:55:06
Line to show. At2021-04-11 01:55:08
Line to show. At2021-04-11 01:55:10 Done.
对于客户端,我们也有简单的代码验证,关注浏览器开发者工具即可1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19function watchResource(url, callback) {
const xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = () => {
if (xhr.readyState >= 3 && xhr.status === 200) {
callback(xhr.responseText);
}
};
xhr.send();
return xhr;
}
watchResource("/streaming.php", function (res) {
console.log(res);
});
在这种情况下,每次后端有新的数据推送过来时,都会触发 xhr.onreadystatechange
事件,进而进入回调函数。
关于 xhr.onreadystatechange 的 readState 状态值含义如下[1]:
1 | 0 The request is not initialized. |
应用场景
HTTP Streaming VS WebSocket
两者的模式和使用场景不同,最明显的区别
- HTTP Streaming 是 Server Push,数据流向是单向的,由服务器推送给客户端。
- WebSocket 是双向通信,客户端可以和服务端进行交互通信。
以容器云为例的应用场景
以容器云平台为例,我们需要在平台上对容器进行简单的管理,其管理包括:
- 场景1:持续获取容器的运行日志,即容器终端持续输出的日志(view container logs)
- 场景2:远程登录至容器终端(exec container terminal)
场景1情况下,我们需要持续获取终端的日志输出,并且数据的流向是单向的:即服务端有新的日志输出就推送至客户端
在这种情况下,我们可以采用 HTTP Streaming 的方式来完成需求,其前端的代码简化如下
1 | // 监听 url 的输出,每次触发后进入 callback 回调 |
场景2情况下,数据是双向交互的,我们选择使用 Websocket
此场景下,用户输入 command 后,服务端响应后返回至前端,是典型的双向通信场景,使用 websocket 来完成需求。
注意点
在实际操作过程中,还是有些注意点需要提及。
- 服务端的数据如果经过 Nginx 转发,需要注意
proxy_buffer
配置。不然会出现服务端服务有持续输出,经过 Nginx 后不一定有输出的情况。 - 客户端在处理完流数据后,记得在合适的析构时期断开连接。如在
unmount
组件时进行xhr.abort()
参考资料
- [1] XMLHttpRequest WIKI https://www.wikiwand.com/en/XMLHttpRequest
- [2] What is HTTP Streaming? https://www.pubnub.com/learn/glossary/what-is-http-streaming/
- [3] proxy_buffer_size http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size