之前也聊了不少网络协议这块内容,现在我们将深入探讨关键的网络协议及其在不同应用中的作用。重点在于理解这些协议如何塑造我们在互联网上的通信和互动方式。我们将深入研究以下领域:
WebSocket
在之前的讨论中,我们研究了HTTP及其在客户端和服务器之间的典型请求-响应交互中的作用。HTTP在大多数情况下表现良好,特别是当响应是即时的时候。然而,在服务器需要主动向客户端推送更新的情况下,尤其是这些更新依赖于客户端无法预测的事件(比如其他用户的操作),HTTP可能不是最有效的方法。这是因为HTTP基本上是一种拉取(pull-based)协议,客户端必须发起所有请求。那么,在不需要客户端预测和请求每个更新的情况下,如何让服务器向客户端推送数据呢?通常有四种处理这种推送型通信的方法,如下图所示。
1. 短轮询(Short polling)
这是最基本的方法。在这种方法中,客户端,通常是运行在我们的浏览器中的Web应用程序,会不断向服务器发送HTTP请求。想象这样的场景:我们登录到一个Web应用程序,并被要求用智能手机扫描一个二维码。这个二维码通常用于某个特定的操作,比如认证或启动某个过程。Web应用程序不知道我们何时会扫描二维码。因此,它会每1-2秒向服务器发送一次请求,以检查二维码的状态。一旦我们用智能手机扫描了二维码,服务器会识别扫描,然后在Web应用程序的下一个检查请求中,将更新后的状态发送回来。这样,我们在扫描二维码后将在接下来的1-2秒内得到响应。由于这种频繁的检查,我们将这种方法称为“短轮询”。
这种方法有两个问题:
•它发送大量的HTTP请求,占用带宽并增加服务器负载。•在最坏的情况下,我们可能要等待长达2秒才能收到响应,导致明显的延迟。
2. 长轮询(Long polling)
长轮询通过设置较长的HTTP请求超时时间来解决短轮询的问题。可以这样理解:我们将超时时间调整为30秒。如果我们在这个时间段内扫描了二维码,服务器将立即发送响应。这种方法显著减少了HTTP请求的数量。
然而,长轮询并非没有挑战。即使长轮询减少了请求的数量,但每个开放的请求仍然需要与服务器保持连接。如果有许多客户端,这可能对服务器资源造成压力。
3. WebSocket
短轮询和长轮询适用于简单的任务,比如扫描二维码。但对于复杂、数据量大且需要实时交互的任务,比如在线游戏,需要一种更高效的解决方案 - 这就是WebSocket。
TCP本质上允许双向数据流,使客户端和服务器能够同时相互发送数据。然而,基于TCP的HTTP/1.1并未充分利用这种能力。在HTTP/1.1中,数据传输通常是按顺序进行的 - 一方发送数据,然后另一方回应。这种设计对于网页交互足够,但对于需要实时交互的在线游戏等应用而言显得不足。WebSocket是另一种基于TCP的协议,在单个连接上允许全双工通信,填补了这个空白。稍后我们将详细介绍。
4. SSE (Server-Sent Events)
SSE,即服务器推送事件,适用于特定的用例。当客户端建立SSE连接时,服务器保持此连接开放以持续发送更新。这种设置非常适用于服务器需要定期向客户端推送数据的情况,而客户端只需接收数据,无需向服务器发送信息。典型的例子是实时股票市场数据更新。使用SSE,服务器可以在每次有更新时向客户端推送实时数据,而无需每次更新都发送请求。值得注意的是,与WebSocket不同,SSE不支持双向通信,因此在需要双向交互的用例中不太适用。
如何建立WebSocket连接
要建立WebSocket连接,我们需要在HTTP头部包含特定字段,这些字段告诉浏览器切换到WebSocket协议。一个随机生成的Base64编码密钥(Sec-WebSocket-Key)被发送到服务器。
请求头:
Connection: Upgrade
Upgrade: WebSocket
Sec-WebSocket-Key: T2a6wZlAwhgQNqruZ2YUyg==
服务器响应头:
HTTP/1.1 101 Switching Protocols
Sec-WebSocket-Accept: iBJKv/ALIW2DobfoA4dmr3JHBCY=
Upgrade: WebSocket
Connection: Upgrade
状态码101表示协议正在切换。经过这个额外的握手后,WebSocket连接建立完成,如下图所示:
WebSocket消息
一旦HTTP升级为WebSocket,客户端和服务器将在帧中交换数据。我们来看一下数据是什么样子的:
操作码(Opcode)是一个4位字段,表示帧数据的类型。
•“1”表示文本帧。•“2”表示二进制帧。•“8”表示关闭连接的信号。
有效负载长度可以是一个7位字段,也可以扩展为包含扩展的有效负载长度。如果这两个长度字段都被充分利用,有效负载长度可以表示几个TB的数据。
WebSocket适用于在线游戏、聊天室和协作编辑应用等需要客户端和服务器频繁交互的场景。
RPC
RPC允许在不同服务上执行函数。从调用程序的角度来看,它似乎是在本地执行函数。下图展示了本地过程调用和远程过程调用之间的区别。我们可以将订单管理和支付等模块部署在同一进程或不同服务器上。当部署在同一进程时,这是本地函数调用。当部署在不同服务器上时,这是远程过程调用。
为什么我们需要RPC?难道我们不能使用HTTP在服务之间进行通信吗?让我们在下表中比较RPC和HTTP。
RPC相对于HTTP的主要优势在于它轻量级的消息格式和卓越的性能。例如,gRPC就是一个例子,它在HTTP/2上运行,由于这一点,它具有更好的性能。
接下来,我们将探讨另一个重要的应用层协议 - RPC(远程过程调用)。
让我们逐步了解gRPC的运作流程:
第一步:客户端发起一个REST调用。请求体通常以JSON格式表示。
第二至四步:订单服务(充当gRPC客户端)接收到REST调用后,将其转换成适当的格式,并且发起一个RPC调用给支付服务。gRPC将客户端存根编码成二进制格式,并将其发送到底层传输层。
第五步:gRPC通过HTTP2将数据包发送到网络。二进制编码和网络优化使得gRPC比JSON快上多达五倍。
第六至八步:支付服务(充当gRPC服务器)接收到数据包后,对其进行解码,并调用服务器应用程序。
第九至十一步:服务器应用程序返回的结果被编码并发送回传输层。
第十二至十四步:订单服务接收到数据包后,对其进行解码,并将结果发送给客户端应用程序。