RPC/HTTP/WebSocket的出现契机 | 青训营笔记

343 阅读7分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 8 天

前言

我们都知道,计算机网络是想要成为一名优秀的开发工程师所必不可少的知识内容。计算机网络的体系结构是分层的,由国际标准组织提出的 OSI 七层网络模型,分别是分别是应用层、表示层、会话层、传输层、网络层、数据链路层以及物理层。但是七层模型过于复杂,因此具体实现的时候采用了 TCP/IP 四层网络模型,分别是应用层、传输层、网络层和网络接口层。

对于从事开发岗位的程序员来说,需要重点掌握的是传输层和应用层的协议。传输层协议就是可靠传输的 TCP 协议和不可靠传输的 UDP 协议,应用层则有基于传输层协议的各种协议,例如 HTTP 协议、FTP 协议等。

对于 HTTP 协议,大家想必是非常熟悉了,包含安全加密的基于 HTTP 的 HTTPS 协议正是当下广泛用于进行客户端和服务端通信的协议。但是在学习了微服务架构以后,我们又得知在微服务内部服务与服务之间进行通信时,主要采用的又是 RPC 协议,那么为什么有了 HTTP 协议还要有 RPC 协议呢,其实 RPC 协议出现的时间比 HTTP 协议还要早,这就要从两个协议的起源开始说起。

TCP/UDP 协议的出现

TCP/UDP 最早是在上世纪 70 年代提出的,以 TCP 协议为例,作为传输层协议,TCP 是面向连接可靠传输基于字节流的。其他两个特点先不谈,正是因为 TCP 协议基于字节流这个特点,使用纯裸的 TCP 就会有粘包问题:字节流可以理解为一个双向的通道里流淌的数据,这个数据就是我们常说的二进制数据,简单来说就是一大堆 01 串。纯裸 TCP 收发的这些 01 串之间是没有任何边界的,接收方根本不知道到哪个地方才算一条完整消息。

因此,纯裸的 TCP 是不能直接拿来用的,需要在这基础之上定义消息的头部、边界、格式等信息,于是,每个基于 TCP 的项目都定义这样一套自己的协议解析标准,它们的原理都类似,由此衍生出了应用层协议。

RPC 协议的诞生

上世纪 80 年代,各大公司已经开始开发桌面端应用,例如 A 公司开发了自己的桌面端应用,它只与 A 公司自己的服务端进行通信,也即 C/S 结构的系统。由于上面提到了纯裸的 TCP 不能直接使用,所以 A 公司基于 TCP 定义了用于自己的桌面端和自己的服务端进行通信的应用层协议,而各个公司开发的这种协议主要进行的都是远程过程调用(Remote Procedure Call),所以统称为 RPC协议 ,可见 RPC 主要指的是一种调用方式,而具体的各种实现才是真正的 RPC协议 ,比较著名的 RPC 协议有 gRPCthrift 协议。所以最初的 RPC 协议主要是给桌面端应用使用的。

HTTP 协议的诞生

上世纪 90 年代,出现了一种特殊的桌面端应用,那就是浏览器,众所周知,浏览器上的网页应用基于 B/S 结构,而且对于浏览器本身,谷歌公司开发的谷歌浏览器不仅要能访问谷歌的服务器,还要能访问其他各个公司的服务器。那么各种各样的 RPC 协议显然就无法满足要求了,于是万维网联盟就制定出了统一的标准协议,即 HTTP 协议 ,因此,使用 HTTP 协议作为通信协议的浏览器可以访问任何同样基于 HTTP 协议作为通信协议的服务器进行通信,互联网也走进人们的视野,迅速发展。

再后来,各大应用都开始支持多端,即网页端、桌面端和移动端等,网页端自然必须使用 HTTP 协议进行通信,不能随意改变,而对于其他端再开发另外的服务器自然没必要了,因此渐渐地就其他端都使用 HTTP 协议进行通信了,RPC 协议就慢慢退居幕后了,只在公司内部的服务之间通信时采用。

那么为什么公司内部通信要用 RPC 协议而不用 HTTP 协议,主要是因为 RPC 协议是 二进制协议 ,而最常见的 HTTP/1.1 协议是 文本协议 ,因此在性能上 RPC 协议会更优。后来在 2015 年,HTTP 2.0 提出,也从文本协议改成了二进制协议,其实 HTTP/2.0 的性能已经超过大部分 RPC 协议了,甚至 gRPC 直接就是基于 HTTP/2.0 进行开发的,但是因为公司内部服务的通信已经使用 RPC 协议跑了许久了,所以基于历史原因也没必要去换了。

WebSocket 协议

我们知道,TCP 协议是支持全双工通信的,也即通信双方的任意一方都可以主动发送消息给另一方。但是 HTTP 协议却做成了半双工通信,也即同一时刻只有一方能发送消息,另一方接受,这点主要是当时在设计 HTTP 协议的时候就是基于请求-响应这样的模型来设计的,因此半双工就足够了。

因此,在使用 HTTP 协议时,如果想要实现服务端“主动”向客户端发送消息的话,一般都是基于客户端轮训的方式实现的,这种方式是一种伪装的服务端主动向客户端发送消息,因为本质上还是客户端在不断发请求问服务端有没有消息要发给它。

虽然轮训可以应对简单的服务端主动发消息给客户端的场景,但是后来出现了网页游戏,网页游戏要求服务端必须频繁主动发送数据包给浏览器客户端,这个时候现有的半双工的 HTTP 协议、RPC 协议就都不能满足要求了,于是就出现了新的WebSocket协议,这个协议就是传输层基于 TPC 、完全支持服务端主动向客户端发送消息的全双工协议了。

一开始浏览器仍然是发送 HTTP 请求,如果在 HTTP 请求的请求头中加上如下字段:

Connection: Upgrade
Upgrade: WebSocket
Sec-WebSocket-Key: T2a6wZlAwhgQNqruZ2YUyg==\r\n

这些字段表示客户端想将协议升级成 WebSocketSec-WebSocket-Key 是一段随机生成的 base64 码,如果服务端支持 WebSocket 协议,那么就会返回一个状态码为 101 的 HTTP 响应,同时根据客户端生成的 base64 码,用某个公开的算法变成另一段字符串,放在 HTTP 响应头的 Sec-WebSocket-Accept 字段里:

HTTP/1.1 101 Switching Protocols\r\n
Sec-WebSocket-Accept: iBJKv/ALIW2DobfoA4dmr3JHBCY=\r\n
Upgrade: WebSocket\r\n
Connection: Upgrade\r\n

状态码 2xx 表示成功、3xx 表示资源重定向、4xx 表示客户端错误、5xx 表示服务端错误是比较常见的,而 101 则是表示协议转换

之后浏览器也将得到的 base64 码使用同样的公开算法进行转换,如果转换后得到的字符串与浏览器响应的一致则验证通过。这样双方的 WebSocket 连接就建立完成了,后续双方就采用 WebSocket 协议的数据格式进行通信了。

值得注意的是,双方虽然在升级协议阶段使用的还是 HTTP 协议,但升级协议完成,建立好 WebSocket 连接后就与 HTTP 协议没有任何关系了。同时,虽然名字中含有 Socket ,但 WebSocket 与操作系统的Socket 系统调用完全是两个东西。