RPC

这是一篇关于 Kitex 这一用于 Go 语言的 RPC 框架的文章。

RPC

Kitex 是字节跳动开源的高性能、强可扩展性的 Go RPC 框架。RPC (Remote Procedure Call,远程过程调用)是一种通过网络从远程计算机上请求服务的技术,使得外显地对一个远程服务的调用就像调用本地函数一样。

前置知识

通信模型

与 RPC 相对的、用于微服务间通信的手段还有基于 HTTP 的 RESTful API。RESTful API 通常使用 HTTP 协议,通过 URL 来指定资源,并使用 HTTP 动词(GET、POST、PUT、DELETE)来表示对资源的操作;因此其是面向资源,比较重但耦合度低的。RPC 则基于更高效的二进制协议的 HTTP/2,强调性能和效率,并且它是面向过程但耦合度高的,但可以提升代码的可维护性和可扩展性。

HTTP:慢且重(HTTP 请求复杂),面向资源(基于 URL 的调用且 HTTP 的无状态),耦合度低(controller 的定义灵活)

RPC:快且轻量(二进制协议),面向过程(基于函数调用),耦合度高(接口的参数和服务的定义都是固定的)

  • 为什么RPC不使用HTTP/3?

    HTTP/3是HTTP协议的最新版本,主要改进在于使用QUIC协议作为传输层协议,QUIC基于UDP而不是TCP,为了保证可靠通信,QUIC需要在应用层实现类似于TCP的机制保证数据传输的可靠性,包括错误检测、流量控制、拥塞控制。

    微服务大多是在内网环境中,网络状态良好,使用长连接、连接多路复用也能减少TCP的建连开销,因此在内网微服务场景下HTTP/3的优势并不明显,反而因为应用层可靠机制的引I入,导致性能劣化。

IO 多路复用

  • select:单个进程能够处理的fd 数量有限;需要轮询所有的文件描述符,效率较低

  • poll:没有了fd 的限制,但依然需要遍历所有文件描述符,效率较低

  • epoll:使用事件通知的机制,只有真正准备好进行 I/O 操作的fd 才会被返回,所以 epoll的效率并不随文件描述符数量的增加而降低。支持边缘触发和水平触发两种模式,提供了更多的灵活性。

使用流程

RPC 调用的过程

  1. 客户端构造请求参数发起调用
  2. 客户端通过服务发现、负载均衡等得到服务端实例地址,并建立连接
  3. 客户端将请求参数序列化成二进制数据,并通过网络发送
  4. 服务器接受数据,并反序列化出请求参数
  5. 服务器的 handler(controller) 接收并处理请求,返回响应结果
  6. 服务器将响应结果序列化成二进制数据,并通过网络发送回复给客户端
  7. 客户端接受数据,并反序列化出响应结果,得到调用的结果

RPC 服务开发的流程

  1. 使用 Thrift 作为语言,定义 IDL 服务接口,包括请求参数、响应参数和服务定义
  2. 使用 Kitex 生成代码,并调用 go mod tidy 导入必要依赖
  3. 服务端开发者编写服务端的 handler,以处理客户端的请求并返回响应结果
  4. 服务端开发者运行服务监听端口,等待客户端的连接以应对处理请求
  5. 客户端开发者编写客户端程序(创建客户端 -> 调用 -> 接收响应),经过服务发现连接上服务端程序,发起请求并接收响应

组成

一般的 RPC 都会被大致分为三个大的部分,一是服务的定义(其中服务可以看作一个类,其中有很多接口,还有请求和响应参数,类似于 controller),二是服务的实现(这是由 Server 服务端实现的,类似于 Service 层),三是服务的调用(这是由 Client 客户端发起的,把暴露出的 RPC 接口当成本地调用一样进行调用即可)。