引言
- 远程过程调用(Remote Procedure Call, RPC)是一种让程序可以像调用本地函数一样调用另一个地址空间中的函数的技术。
- RPC 技术广泛应用于微服务架构、分布式系统以及需要跨进程调用的应用场景中。Go 语言自带了 net/rpc 和 net/rpc/jsonrpc 包,分别支持基于文本和 JSON 格式的 RPC 通信。
1. 使用 net/rpc实现 RPC 服务
1.1 文件结构
└─rpc
├─text-rpc-client
│ main.go
│
└─text-rpc-server
main.go
1.2 RPC 服务端
首先定义服务端需要提供的服务接口,并实现该接口的具体方法。
// text-rpc-server/main.go
package main
import (
"log"
"net/http"
"net/rpc"
)
type Arith int
func (t *Arith) Multiple(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
type Args struct {
A, B int
}
func main() {
arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
log.Println("Starting RPC server on :1234...")
log.Fatal(http.ListenAndServe(":1234", nil))
}
在这个例子中,我们定义了一个名为 Arith 的结构体,并实现了一个 Multiple 方法,该方法接收两个整数参数,并返回它们的乘积。
1.3 RPC 客户端
接下来,编写客户端代码来调用服务端提供的方法。
// text-rpc-client/main.go
package main
import (
"log"
"net/rpc"
)
type Args struct {
A, B int
}
func main() {
client, err := rpc.DialHTTP("tcp", "localhost:1234")
if err != nil {
log.Fatal("dialing:", err)
}
defer client.Close()
args := Args{8, 9}
var reply int
err = client.Call("Arith.Multiple", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
log.Printf("Arith: %d*%d=%d", args.A, args.B, reply)
}
客户端首先尝试连接到指定的 RPC 服务端,然后调用 Arith.Multiple 方法,并接收返回的结果。
2. 使用 net/rpc/jsonrpc实现 JSON-RPC 服务
JSON-RPC 是一种轻量级的 RPC 协议,使用 JSON 来编码调用请求和响应。Go 语言同样提供了支持 JSON-RPC 的库。
2.1 文件结构
└─rpc
├─json-rpc-client
│ main.go
│
└─json-rpc-server
main.go
2.2 JSON-RPC 服务端
实现 JSON-RPC 服务端只需要稍微修改之前的代码。
// json-rpc-server/main.go
package main
import (
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
type Args struct {
A, B int
}
type Arith int
func (t *Arith) Multiple(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
func main() {
l, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatal("listen error:", err)
}
arith := new(Arith)
rpc.Register(arith)
for {
conn, err := l.Accept()
if err != nil {
log.Fatal("accept error:", err)
}
go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
}
}
这里的 l.Accept 监听到新连接后,将连接使用指定编码器 jsonrpc.NewServerCodec 封装后传入 rpc.ServeCodec 并启动协程。
2.3 JSON-RPC 客户端
同样,客户端也需要稍作调整以支持 JSON-RPC。
// json-rpc-client/main.go
package main
import (
"fmt"
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
type Args struct {
A, B int
}
func main() {
conn, err := net.Dial("tcp", ":1234")
if err != nil {
log.Fatal("dial error:", err)
}
client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))
args := &Args{8, 9}
var reply int
err = client.Call("Arith.Multiple", args, &reply)
if err != nil {
log.Fatal("Multiple error:", err)
}
fmt.Printf("Multiple: %d*%d=%d\n", args.A, args.B, reply)
}
客户端通过 net.Dial 连接到 JSON-RPC 服务端,将连接 conn 使用指定编码器 jsonrpc.NewClientCodec 封装后传入 rpc.NewClientWithCodec 发起调用。
3. 注意事项
- 3.1 自定义编解码器
你可以使用自定义的编解码器来支持除默认的文本和 JSON 格式之外的数据格式以便提高数据安全性。
- 3.2 安全性和认证
在生产环境中,RPC 服务应该实现安全措施,例如使用 TLS 加密和身份验证机制。
- 3.3 超时和重试
处理网络不稳定的情况,可以通过设置超时时间和重试机制来增强服务的可靠性。