Skip to content
0

RPC

RPC(远程过程调用)是一种用于实现分布式系统通信的协议和机制。它允许在不同的计算机或进程之间通过网络进行相互调用和数据交换,就像调用本地函数一样。在 RPC 中,客户端应用程序可以通过发送请求消息来调用远程服务器上的函数或服务,然后服务器执行相应的操作,并将结果返回给客户端。 RPC 隐藏了底层通信的细节,使得远程调用过程对开发人员来说更加透明和方便

从 TCP 说起

TCP 是面向连接、可靠的、基于字节流的传输层协议

因为 TCP 是基于字节流的,所以不能被直接拿来用,我们需要在 TCP 的基础上加入一些自定义的规则,用于区分消息边界,比如说我们会加上消息头,用于说明包的完整的长度是多少,便于读取的时候,在正确的位置截取我们想要的包

基于这一点,在应用层诞生了非常多的协议,比如 HTTP 和 RPC

HTTP

http 常出现在浏览器访问网页的过程,浏览器发送请求,指定 URL 和方法等。服务器响应请求返回数据

RPC 协议

RPC 不是一个具体的协议,而是一种调用方式,基于 TCP 实现的各种 RPC 协议,都只是定义了不同消息格式的应用层协议而已

使用 RPC 就好像在本地调用远程的一个方法,屏蔽了网络的各种细节

值得一提的是 RPC 不一定得基于 TCP 协议,也可以基于 HTTP、UDP 协议,也可以做到类似的功能

历史

我们通常是接触 HTTP 再接触 RPC,但其实从历史来看,RPC(80年代) 的历史是要早于 HTTP(90年代)的

为什么要有 HTTP 而不止于 RPC?

现在我们电脑上有的各种软件,它们都作为客户端和服务的通信的,这种情况下,由于客户端和服务端都是自家的,自然可以封装各种自己的 RPC 协议,只管自己的客户端和服务端的通信(C/S架构)

但是在浏览器出现之后,浏览器不仅要跟浏览器厂商的服务端通信,还需要和第三方公司的服务端通信,这个时候就需要一个通用的标准的协议。于是,HTTP 就是那个时代用于统一(B/S架构)的协议

HTTP 主要用于 B/S 架构,而 RPC 更多用于 C/S 架构。但现在其实已经没分那么清了,B/S 和 C/S 在慢慢融合

与 HTTP 的区别

服务发现

要想某个服务器发送请求,得先建立连接,建立连接的前提就是知道服务器的 IP 地址和端口。所以需要一种服务来记录映射关系

在 HTTP 中,就是 DNS 了,通过向 DNS 服务端请求得到域名对应的服务地址

而在 RPC 中,也有类似的中间服务去保存服务名和 IP 信息。由于 DNS 也是服务发现的一种,所以也有基于 DNS 去做服务发现的组件,比如 CoreDNS

底层连接形式

以主流的 HTTP1.1 为例子,其默认在建立 TCP 连接后会一直保持这个链接(长链接),之后的请求都会复用这条链接

RPC 也是类似的,也是通过建立 TCP 长链接进行数据交互,不同的是,RPC 还会建立一个连接池,在请求量大的时候,将多条链接放在池内,要发数据的时候再从池里取出来发送,用完再放回去,下次再复用

传输的内容

基于 TPC 的传输的消息,说到底无非都是消息头(Header)和消息体(Body)。

早期 HTTP 是从来传输文本的,所以它传的内容以字符串为主。Header 和 Body 都是如此。在 Body 这块,它使用 Json 来序列化结构体数据

例如,我们搭建一个简易的 HTTP 服务器

import { createApp, createRouter, eventHandler, getHeaders, readBody, readRawBody, sendNoContent, toNodeListener } from 'h3'
import { createServer } from 'http'

const app = createApp()

const router = createRouter()
  .post(
    '/',
    eventHandler(async event => {
      console.log('headers', getHeaders(event))
      console.log('body', await readBody(event))
      sendNoContent(event)
    })
  )

app.use(router)

createServer(toNodeListener(app)).listen(3000, () => {
  console.log('http://localhost:3000')
})

curl 访问这个接口,发起 HTTP 请求

curl -X POST -H 'Content-Type: application/json' -d '{"str":"hello world"}' http://localhost:3000

得到如下输出

headers {
  host: 'localhost:3000',
  'user-agent': 'curl/7.87.0',
  accept: '*/*',
  'content-type': 'application/json',
  'content-length': '21'
}
body { str: 'hello world' }

可以看到 header 的内容比我们核心的内容都要长,也就是说 HTTP 请求的有效数据率有时候很小

RPC,因为它定制化程度更高,可以采用体积更小的 Protobuf 或其他序列化协议去保存结构体数据,同时也不需要像 HTTP 那样考虑各种浏览器行为,比如 302 重定向跳转啥的

HTTP 的请求:

RPC 的请求:

更多区别

  • 纯裸 TCP 是能收发数据,但它是个无边界的数据流,上层需要定义消息格式用于定义消息边界。于是就有了各种协议,HTTP 和各类 RPC 协议就是在 TCP 之上定义的应用层协议。
  • RPC 本质上不算是协议,而是一种调用方式,而像 gRPCThrift 这样的具体实现,才是协议,它们是实现了 RPC 调用的协议。目的是希望程序员能像调用本地方法那样去调用远端的服务方法。同时 RPC 有很多种实现方式,不一定非得基于 TCP 协议。
  • 从发展历史来说,HTTP 主要用于 B/S 架构,而 RPC 更多用于 C/S 架构。但现在其实已经没分那么清了,B/SC/S 在慢慢融合。很多软件同时支持多端,所以对外一般用 HTTP 协议,而内部集群的微服务之间则采用 RPC 协议进行通讯。
  • RPC 其实比 HTTP 出现的要早,且比目前主流的 HTTP/1.1 性能要更好,所以大部分公司内部都还在使用 RPC
  • HTTP/2.0HTTP/1.1 的基础上做了优化,性能可能比很多基于TCPRPC 协议都要好,所以RPCHTTP协议各有使用场景

Released under the MIT License.