RPC

简介

rpc

  • 远程过程调用(Remote Procedure Call,RPC)是一个计算机通信协议
  • 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程
  • 如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用

golang 中实现rpc

  • golang中实现RPC非常简单,官方提供了封装好的库,还有一些第三方的库
  • golang官方的net/rpc库使用encoding/gob进行编解码,支持tcp和http数据传输方式,由于其他语言不支持gob编解码方式,所以golang的RPC只支持golang开发的服务器与客户端之间的交互
  • 官方还提供了net/rpc/jsonrpc库实现RPC方法,jsonrpc采用JSON进行数据编解码,因而支持跨语言调用,目前jsonrpc库是基于tcp协议实现的,暂不支持http传输方式
  • 例题:golang实现RPC程序,实现求矩形面积和周长

server:

注意

  • 结构体字段首字母要大写,可以别人调用
  • 函数名必须首字母大写
  • 函数第一参数是接收参数,第二个参数是返回给客户端的参数,必须是指针类型
  • 函数还必须有一个返回值error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
type Param struct {
High, Width int
}

type Rect struct{}

func (rect *Rect) Area(param Param, ret *int) error {
*ret = param.High * param.Width
return nil
}

func (rect *Rect) Parimeter(param Param, ret *int) error {
*ret = 2 * (param.High + param.Width)
return nil
}

func main() {
// 1. 注册rpc服务
rect := new(Rect)
rpc.Register(rect)
// 2. 通过rpc协议在指定端口进行监听
rpc.HandleHTTP()
// 3. 监听端口
err := http.ListenAndServe(":18000", nil)
if err != nil {
log.Panicln(err)
}
}

client:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
type Param struct {
High, Width int
}

func main() {
// 1. 连接rpc服务
client, err := rpc.DialHTTP("tcp", ":18000")
if err != nil {
log.Panicln(err)
}
// 2. 调用远程方法
param := &Param{50, 100}
var ret int
err = client.Call("Rect.Area", param, &ret)
if err != nil {
log.Panicln(err)
}
log.Println("Area:", ret)
err = client.Call("Rect.Parimeter", param, &ret)
if err != nil {
log.Panicln(err)
}
log.Println("Parimeter:", ret)

}

另外,net/rpc/jsonrpc库通过json格式编解码,支持跨语言调用

rpc 的调用流程

  • 微服务架构下数据交互一般是对内 RPC,对外 REST
  • 将业务按功能模块拆分到各个微服务,具有提高项目协作效率、降低模块耦合度、提高系统可用性等优点,但是开发门槛比较高,比如 RPC 框架的使用、后期的服务监控等工作
  • 一般情况下,我们会将功能代码在本地直接调用,微服务架构下,我们需要将这个函数作为单独的服务运行,客户端通过网络调用

网络传输数据格式

两端需要约定好数据包的格式,成熟的rpc 框架会有自定义的传输协议,这里网络传输格式如下,前面是固定的长度消息头,后面是变长消息体

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package rpc

import (
"encoding/binary"
"io"
"net"
)

// 测试网络中读写数据的情况

// 会话连接的结构体
type Session struct {
conn net.Conn
}

// 构造方法
func NewSession(conn net.Conn) *Session {
return &Session{conn: conn}
}

// 向连接中去写数据
func (s *Session) Write(data []byte) error {
// 定义写数据的格式
// 4字节头部 + 可变体的长度
buf := make([]byte, 4+len(data))
// 写入头部,记录数据长度
binary.BigEndian.PutUint32(buf[:4], uint32(len(data)))
// 将整个数据,放到4后边
copy(buf[4:], data)
_, err := s.conn.Write(buf)
if err != nil {
return err
}
return nil
}

// 从连接读数据
func (s *Session) Read() ([]byte, error) {
// 读取头部记录的长度
header := make([]byte, 4)
// 按长度读取消息
_, err := io.ReadFull(s.conn, header)
if err != nil {
return nil, err
}
// 读取数据
dataLen := binary.BigEndian.Uint32(header)
data := make([]byte, dataLen)
_, err = io.ReadFull(s.conn, data)
if err != nil {
return nil, err
}
return data, nil
}

编写rpc 服务端

  • 服务端接收到的数据需要包括什么?
    • 调用的函数名、参数列表,还有一个返回值error类型
  • 服务端需要解决的问题是什么?
    • Map维护客户端传来调用函数,服务端知道去调谁
  • 服务端的核心功能有哪些?
    • 维护函数map
    • 客户端传来的东西进行解析
    • 函数的返回值打包,传给客户端

RPC
https://tsy244.github.io/2024/08/14/go/RPC/
Author
August Rosenberg
Posted on
August 14, 2024
Licensed under