背景
最近在调研 Web 前端如何可以和后端进行双向的通信,这其实是个非常有价值的话题,或者说是个非常难且不断被提及的话题。
例如在网络质量差的得情况下,如果还是用原始的请求响应模型,那么对于前端的用户体验是极差的,尤其是现在很多 SPA 的情况下,API 的即时性更为重要。除了网络差的情况,后端也有推送的需求,例如前端请求了一个非常耗时的请求,很多时候这种需求都会做成异步的,那么就有了等 Server 侧完成处理之后通知前端的需求,当然,你也可以让前端 polling 去查询结果,但是这个对前端的编码和用户体验都是不友好的。
所以,基于这些背景,我就调研前端和后端目前有哪些可能的技术方案选型,能够更好地支持业务的开发,但是好像结果还是比较不理想,也就有 websocket 这么一项,有可能我没有注意到一些更新的技术,如果你有相关的资料,欢迎留言交流一下。
http2 push
不再支持了。
SSE(Server Send Events)
- 浏览器代码:
const evtSource = new EventSource("/v1/stream/topic");
evtSource.onmessage = function(event) {
// handle event
}
感受:
- pros:
- 比 websocket 简单
- 并且支持自动重传(chrome 下每 3s 发起一次重试,一直重试)
- cons:
- 单向传输(server -> client)
Streaming API
总结:做不到同时发送和同时接收;
可以做到:
- 一个
fetch
专门用于发送,另外一个fetch
专门用于接收,中间用一个 ID 标识连接起来; - 引用文档:
websocket
总结:可以做到同时发送和同时接收
grpc web
截止到目前为止,gRPC Web 只支持两种类型的调用,分别是:
- Unary RPC 调用(一个请求一个响应模型)
- Server Streaming 调用(一个请求,但是多个响应模型)
但是,并不是说你可以直接用 gRPC Web 直接调用一个普通的其他语言的 gRPC Server,因为他们协议是不一样的,所以中间需要一个 gRPC Web 代理,官方推荐的就是 Envoy,并且官方也将 Envoy 作为官方的实现方式进行迭代。
实现的原理:请求是通过 HTTP Post 请求发起的,然后被后端的 Envoy 接收,然后转换成通用的 gRPC 协议;然后在收到响应之后,然后再转换成 Post 的响应,但是 Post 的响应是一个编码后的字符串,还需要进行解码,这个在前端通过 gRPC Web 库实现。
扩展思路
XHR + chunked response
var last_index = 0;
var xhr = new XMLHttpRequest();
xhr.open("GET", "/servers/scan");
xhr.onprogress = function () {
var curr_index = xhr.responseText.length;
if (last_index == curr_index) return;
var s = xhr.responseText.substring(last_index, curr_index);
last_index = curr_index;
console.log("PROGRESS:", s);
};
xhr.send();
- 也不是不行,但是比较麻烦,而且可能有性能问题,因为 xhr 会累加 responsetext,所以需要使用下标来记录已经读取了哪些数据。
一些过程资料
- 在 Streaming API 的引用文章里面:Fetch Streaming Requests,这篇文章是 Chrome 团队的官网文章,里面直接就说了,除非你完全发送完 request body,否则你不会接收到任何响应。
- 但是,里面也说了,HTTP 协议是允许在只发送了部分请求时就接收响应的,但是没有浏览器实现这个,有待验证。
- 但是这个条件应该只限制与普通的 GET/POST.. 等请求,对于 websocket 是不适用的,因为 websocket 就是用于 Browser 和 Server 之间类 socket 通信的;
结论:无阻碍双向通信还是只有 websocket。