据上课所知,RPC(Remote Procedure Call)远程过程调用,可以让调用者像本地调用一样调用远端服务器的方法,是分布式系统下常见的一种通信方式。本篇笔记是我对 RPC 协议实现的思考,开发环境为 go1.20。
PRC 三个问题:①函数映射 ②数据转换字节流 ③网络传输。解决这三个问题就能实现 RPC 协议。
下面我们将调用者记为 Client,将远端服务器记为 Server,令要调用的函数为 Add(a,b int)实现两数加减,用 JSON协议 和HTTP协议 进行网络传输。
- 即 Client 向 Server 发起 HTTP 请求,url中包含 add 方法和参数,解决 RPC 第一个问题 ①函数调用;
- 使用 JSON协议 解决 RPC 第二个问题 ②数据转换字节流;
- HTTP 则解决 RPC ③网络传输问题。
下面是 Server 端实现代码。在远端服务器 Server 上设置一个 HandleFunc 接收 HTTP 的 Get 请求,请求包含 Add 方法和参数:
func main() {
//http://127.0.0.1:8000/add?a=1&b=2 RPC调用的URL
//返回的格式化: json{"data":3}
http.HandleFunc("/add", func(w http.ResponseWriter, r *http.Request) {
_ = r.ParseForm() // 解析参数, 忽略错误变量
//fmt.Println("path:", r.URL.Path)
a, _ := strconv.Atoi(r.Form["a"][0]) // 提取URL中的第一个变量a,并转为int类型
b, _ := strconv.Atoi(r.Form["b"][0]) // 提取URL中的第一个变量b,并转为int类型
w.Header().Set("Content-Type", "application/json") // 写入该请求头,表示传输的是JSON数据
// 将JSON数据
jData, _ := json.Marshal(map[string]int{
"data": a + b,
})
_, _ = w.Write(jData) // 写入数据
})
_ = http.ListenAndServe(":8000", nil) // 监听 Client端的远程调用请求
}
下面是 Client 端实现代码。在客户端 Client 上创建一个 Add方法,并在 main 中本地调用。
//rpc 远程调用,如何做到像本地调用
// 接收 JSON 数据的结构体
type ResponseData struct {
Data int `json:"data"`
}
// 模拟本地调用,实际忽略底层的网络传输
func Add(a, b int) int {
//传输协议:HTTP
// 向远端服务器 Server 发起 add 方法请求
req := HttpRequest.NewRequest() // 用了这个库"github.com/kirinlabs/HttpRequest",可以自行用http包实现
res, _ := req.Get(fmt.Sprintf("http://127.0.0.1:8000/%s?a=%d&b=%d", "add", a, b))
body, _ := res.Body() // 得到数据是JSON格式
//fmt.Println(string(body))
resData := ResponseData{}
_ = json.Unmarshal(body, &resData) // 将JSON数据解码为结构体
return resData.Data
}
func main() {
result := Add(1, 2) // 1 + 2,模拟本地调用,实际忽略底层的网络传输
fmt.Println("输出结果:", result) // 输出结果:3
}
以上。