RPC 笔记(01)— RPC概念、调用流程、RPC 与 Restful API 区别

1. 基本概念

PRC 远程过程调用 Remote Procedure Call,其就是一个节点请求另外一个节点提供的服务。当两个物理分离的子系统需要建立逻辑上的关联时,RPC 是牵线搭桥的常见技术手段之一。除 RPC 之外,常见的多系统数据交互方案还有分布式消息队列、HTTP 请求调用、数据库和分布式缓存等。

  • 本地过程调用:
    如果要将本地的对象进行相关操作,可以定义一个方法,然后将相关对象传入,然后对其对象进行更新,然后由函数返回更新后的函数对象。

  • 远程过程调用:
    在上述过程中,如果其定义的函数是在另外一个服务器端,并且执行的函数体也是在另外一台远程服务器上,那么这个过程就称之为远程过程调用。

2. 调用过程

其调用过程如下所示:
1

  1. 首先客户端需要告诉服务器端,需要调用的函数,然后这里的函数和进程 ID 存在一个映射,客户端远程调用的时候,需要查询一下函数,找到对应的 ID,然后执行对应函数的代码。
  2. 客户端需要把本地参数传递给远程函数,在本地调用的过程中,会通过把参数转换为字节码,然后再传递给服务端,最后服务端再转换为自身可以读取的格式,这是一个序列化和反序列的过程。
  3. 转换完成数据以后,在进行传输的过程一般是使用 HTTPSocket 协议进行传输。

除去 RPC 调用以外,还有 RESTful API 进行调用。RESTful API 调用其示例图如下图所示:
2

在上图中,一个服务 A 如果访问另外一个服务器下的 B,这个过程采用的就是 RESTful API 进行数据的传输。并且两个过程的数据会进行序列化和反序列化炒作。

两种方式进行对比而言,

  • RPC 通信代价比较低,因为 RPC 是直接基于 TCP 进行调用的,并且传输的数据都是基于 TCP 进行的,所以效率更高,更优。但是由于是基于 TCP 的所以实现起来更为的复杂,更为的难以维护。
  • RESTful API 由于是直接基于 HTTP 的所以实现更为简单,维护更为容易。
  • RPC 快,效率高,但是不够通用,就好比地方方言,HTTP通用是普通话,但是效率不够高,传输的字节内容冗余多。
  • REST 相对更规范,更标准,更通用,简单易用,维护性和扩展性都比较好。
  • RPC+Protobuf 采用的是 TCP 做传输协议,REST 直接使用 HTTP 做应用层协议,这种区别导致 REST 在调用性能上会比 RPC+Protobuf 低。

Go 语言中常用的 API 风格是 RPCREST,常用的媒体类型是 JSONXMLProtobuf。在 Go API 开发中常用的组合是 gRPC+ProtobufREST+JSON

其实业界普遍采用的做法是,内部系统之间调用用 RPC,对外用 REST,因为内部系统之间可能调用很频繁,需要 RPC 的高性能支撑。对外用 REST 更易理解,更通用些。当然以现有的服务器性能,如果两个系统间调用不是特别频繁,对性能要求不是非常高,REST 的性能完全可以满足。

HTTP 调用其实也是一种特殊的 RPC

HTTP1.0 协议时,HTTP 调用还只能是短链接调用,一个请求来回之后连接就会关闭。HTTP1.1HTTP1.0 协议的基础上进行了改进,引入了 KeepAlive 特性可以保持 HTTP 连接长时间不断开,以便在同一个连接之上进行多次连续的请求,进一步拉近了 HTTPRPC 之间的距离。

httpHTTP 协议进化到 2.0 之后,Google 开源了一个建立在 HTTP2.0 协议之上的通信框架直接取名为 gRPC,也就是 Google RPC

3. 解决问题

总之,RPC 解决掉了在分布式系统之中,服务之间调用的问题。让服务在远程调用的时候,能够像本地调用一样非常方便,并且让调用者感知不到远程调用的具体逻辑。

RPC 只是对底层通信和交互协议的一个封装,便于上层使用。

4. 调用流程

一般来说调用过程如下所示:
3

  1. 调用者,以本地调用方式发起调用。
  2. Client stub 客户端收到调用以后,将会把被调用的方法名,参数进行打包编码成为特定的格式,包装成为能够被网络传输的消息体。
  3. Client stub 将消息体通过网络发送给服务端。
  4. Server stub 将会收到通过网络接收到的消息后按照相应的格式进行拆分,获取方法名称和及其调用传入的参数。
  5. 被调用者 Server 本地调用执行后将会把结果返回给 Server stub
  6. Server stub 将会将返回值打包编码成为消息,然后通过网络发送给客户端。
  7. Client stub 收到消息以后,将会进行拆分,然后返回给 CLient
  8. Client 最终获得本地 RPC 调用结果。

RPC 传输控制

对于消息数据的传输,主要有 HTTP 传输和 TCP 传输,一般来说 RPC 使用 TCP 进行传输,因为 TCP其要优于 HTTP 传输。

传输过程如下图所示:
6
传输的时候一般通过 Socket 接口进行传输,也有使用 HTTP 作为底层的传输的结果,但是使用 HTTP 传输的比较少。

其过程如下:

  1. Server 发起创建套接字请求。
  2. 创建套接字后绑定相关端口
  3. 监听链接
  4. 进入接收连接状态中。
  5. Client 端创建套接字
  6. Client 端和 Server 端刚刚建立完成的 Socket 进行连接的建立。
  7. Server 端进行读取,Client 进行数据写。
  8. Client 端进行数据读取,
  9. 双方交换完数据以后,关闭连接。


Server 端代码示例:

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(("localhost", 8080))
sock.listen(1)  # 监听客户端连接
while True:
    conn, addr = sock.accept()  # 接收一个客户端连接
    print(conn.recv(1024))
    conn.sendall(b"world")  # 将响应发送到发送缓冲 send buffer
    conn.close()

输出结果

b'hello'

Client 端代码示例:

import socket

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("localhost", 8080))
sock.sendall(b"hello")  # 将消息输出到发送缓冲 send buffer
print(sock.recv(1024))
sock.close()

输出结果

b'world'

问题:客户端疯狂发送请求,但是服务器不读不处理,会发生什么?

  • 如果接收和发送队列没有设置大小,服务器处理能力弱,tcp 会动态调整直至耗尽整个内存;
  • 设置了大小,socket 缓冲区满,那么 socket 会出现阻塞,不接受发送端的消息;
  • 如果发送的请求 size 大于发送和接收队列之和,那么会一直阻塞下去;

5. 远程调用时序图

9

  1. 本地 RPCTest 类,将会调用 ServiceProducer 在服务端注册并启动 Server
  2. Server 将会调用 registerstart 进行初始化应用。
  3. RPCTest 将会通过 Client 调用本地方法。
  4. Client 将会调用远程服务器方法 ServiceProducer 并传入本地阐述,将消息发送给 Server端。
  5. Server 端放过反射,压入 ServerTask
  6. ServerTask 将会远程执行方法。
  7. 最后调用栈,调用完成以后,将结果返回给 Server
  8. Server 通过网络传输信息到 Client
  9. Client 把结果最终返回给 RPCTest

版权声明:本文为wohu1104原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>