Golang RPC框架详解 | 青训营

118 阅读5分钟

基础知识:

Go语言的RPC(Remote Procedure Call,远程过程调用)是一种用于在分布式系统中进行通信的技术。它允许你调用远程机器上的函数,就像调用本地函数一样,隐藏了网络通信的细节。Go标准库中提供了net/rpc包来支持RPC。

RPC框架的基本原理:

定义共享接口:

在RPC中,首先你需要定义一个共享的接口,其中包含要在远程服务器上调用的方法。这个接口通常位于客户端和服务器的共享代码中。

实现服务器:

在服务器端,你需要实现这个接口的方法,并将其注册到RPC服务中。这使得客户端可以通过RPC调用这些方法。

创建客户端:

在客户端,你可以使用rpc.Dial函数连接到远程服务器。这会返回一个连接,允许你通过连接调用服务器上的方法。

远程调用:

一旦连接建立,你可以使用连接上的方法调用服务器上的方法。这些方法调用会在客户端和服务器之间进行序列化和反序列化。

使用RPC的基本步骤:

1. 定义共享接口:

在 Go 的 RPC 中,首先你需要定义一个共享接口,其中包含要在客户端和服务器之间调用的方法。该接口将被客户端和服务器共享,因此通常将其定义在共享的代码库中,以便双方都可以访问。

type MathService interface {
    Add(args Args, reply *int) error
}

2. 实现服务器:

在服务器端,你需要实现上述接口的方法,即 Add 方法。这个方法将会在客户端调用时被执行。

type MathServiceImpl struct{}
func (m *MathServiceImpl) Add(args Args, reply *int) error {
    *reply = args.A + args.B
    return nil
}
mathService := new(MathServiceImpl)
rpc.Register(mathService)

在这里,我们定义了一个结构体 MathServiceImpl 并实现了 MathService 接口的 Add 方法。然后,通过 rpc.Register 函数将 mathService 注册为可供客户端调用的 RPC 服务。

3. 创建客户端:

在客户端,你需要使用 rpc.Dial 函数来建立与远程服务器的连接。

client, err := rpc.Dial("tcp", "localhost:1234")
if err != nil {
    log.Fatal("dialing:", err)
}
defer client.Close()

在这里,我们使用 rpc.Dial 函数创建一个与服务器的连接。在完成之后,需要调用 client.Close() 来关闭连接。

4. 远程调用:

一旦建立了与服务器的连接,你可以使用 Call 方法来调用服务器上的方法。

args := Args{A: 2, B: 3}
var reply int
err = client.Call("MathService.Add", args, &reply)
if err != nil {
    log.Fatal("arith error:", err)
}
fmt.Println("Result:", reply)

在这里,我们使用 client.Call 来调用远程的 Add 方法。传递的参数包括方法名称、输入参数和输出参数。如果有错误发生,会将错误信息捕获,否则会输出计算结果。

小结:

这就是使用 Go 语言的 RPC 框架的基本步骤。你需要确保共享接口、实现服务器端方法、创建客户端连接,并进行远程调用。

我们把上述过程组合起来:

1. 创建服务器端代码:

首先,你需要创建一个服务器端的 Go 代码,用于注册 RPC 服务和提供方法供客户端调用。可以按照之前的例子编写一个简单的服务器端代码,比如下面的示例:

// server.go
package main
import (
	"fmt"
	"log"
	"net"
	"net/rpc"
)
type MathService struct{}
type Args struct {
	A, B int
}
func (m *MathService) Add(args Args, reply *int) error {
	*reply = args.A + args.B
	return nil
}
func main() {
	mathService := new(MathService)
	rpc.Register(mathService)

	listener, err := net.Listen("tcp", ":1234")
	if err != nil {
		log.Fatal("listen error:", err)
	}

	fmt.Println("Server listening on port 1234")
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Fatal("accept error:", err)
		}
		go rpc.ServeConn(conn)
	}
}

2. 创建客户端代码:

然后,你需要创建一个客户端的 Go 代码,用于连接到服务器并调用远程方法。可以按照之前的例子编写一个简单的客户端代码,比如下面的示例:

// client.go
package main
import (
	"fmt"
	"log"
	"net/rpc"
)
type Args struct {
	A, B int
}
func main() {
	client, err := rpc.Dial("tcp", "localhost:1234")
	if err != nil {
		log.Fatal("dialing:", err)
	}
	defer client.Close()
	args := Args{A: 2, B: 3}
	var reply int
	err = client.Call("MathService.Add", args, &reply)
	if err != nil {
		log.Fatal("arith error:", err)
	}
	fmt.Println("Result:", reply)
}

3. 运行代码:

将上述的服务器端代码保存为 server.go,将客户端代码保存为 client.go。然后,分别在两个不同的终端窗口中运行以下命令来启动服务器和客户端:

在一个终端窗口中运行服务器:

go run server.go

在另一个终端窗口中运行客户端:

go run client.go

你应该会在客户端终端窗口中看到打印出的计算结果。通过这个过程,你可以在自己的电脑上验证 Go 语言的 RPC 过程是否能够正常工作。

最后解释一下上面代码中出现的TCP/IP到底代表什么:

在 Go 的 RPC 例子中,rpc.Dial 函数用于建立与服务器的连接,它接受两个参数:网络协议和主机地址。

"tcp":

这是网络协议的指示符,表示使用 TCP/IP 协议进行通信。

":1234" 或 "localhost:1234":

这是主机地址和端口号。在这里,":1234" 表示在所有可用的网络接口上监听端口 1234,而 "localhost:1234" 表示连接到本地主机的端口 1234。

代码修改:

在示例中,你可以根据你的需求选择使用其中之一。如果你想在本地测试,可以使用 "localhost:1234"。如果你想在网络上公开服务器,可以使用 ":1234"。这只关系到客户端代码中如下部分的修改。

本地测试:

client, err := rpc.Dial("tcp", "localhost:1234")

远程调用测试:

(假设服务器的 IP 地址为 "123.123.1.0")

client, err := rpc.Dial("tcp", "123.123.1.0:1234")

小结:

通过这些修改,你的客户端代码将能够连接到服务器并进行远程调用,无论服务器是否在本地还是在远程。

总结:

我们通过测试了解了prc框架的实际使用,可以看出在Golang中,rpc框架并不是繁琐而难以理解的东西。相比较于一些别的语言的后端框架,简单易上手很多。大家快去试一下吧。