Golang的net/rpc包提供高效、强类型的RPC通信机制,适用于高性能微服务内部通信。通过定义共享接口(如Arith服务的Multiply方法),在服务器端注册服务并监听连接,客户端通过Dial建立连接后可同步或异步调用远程方法。相比RESTful API,RPC使用二进制编码(如gob),性能更高、延迟更低,适合对性能敏感的内部服务通信;而REST因基于HTTP、易于调试和跨语言兼容,更适合公共API。实现时需遵循方法导出、两个参数(请求和指针响应)、返回error等规则,并通过rpc.Register注册服务。生产环境中需结合context实现超时控制,合理处理错误(如自定义错误类型),复用rpc.Client实例以减少连接开销,并设计重连机制应对网络不稳,确保系统健壮性。

Golang的
net/rpc
要搭建一个Golang RPC客户端与服务器的完整示例,我们需要三个核心部分:一个共享的接口定义(通常是结构体及其方法),一个服务器端实现,以及一个客户端调用。
首先,我们定义一个共享的服务接口。为了简单起见,我们创建一个
Arith
Multiply
shared/arith.go
立即学习“go语言免费学习笔记(深入)”;
package shared
// Args 定义了乘法运算的两个操作数
type Args struct {
A, B int
}
// Quotient 定义了除法运算的结果(如果我们要扩展的话)
type Quotient struct {
Quo, Rem int
}
// Arith 是我们的RPC服务接口
type Arith int // 只是一个占位符,实际方法会绑定到这个类型上接着,我们实现服务器端。服务器会注册
Arith
server/main.go
package main
import (
"log"
"net"
"net/rpc"
"time"
"your_module_path/shared" // 替换为你的模块路径
)
// Arith 是服务的实际实现
type Arith int
// Multiply 方法接收Args结构体作为参数,将结果写入reply,并返回error
func (t *Arith) Multiply(args *shared.Args, reply *int) error {
log.Printf("Server received Multiply request: %d * %d", args.A, args.B)
*reply = args.A * args.B
time.Sleep(100 * time.Millisecond) // 模拟一些工作负载
return nil
}
func main() {
arith := new(Arith)
rpc.Register(arith) // 注册服务实例
tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
if err != nil {
log.Fatalf("Error resolving TCP address: %v", err)
}
listener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
log.Fatalf("Error listening on port: %v", err)
}
log.Println("RPC Server started on port 1234")
for {
conn, err := listener.Accept()
if err != nil {
log.Printf("Accept error: %v", err)
continue
}
// 为每个新连接启动一个goroutine来处理RPC请求
go rpc.ServeConn(conn)
}
}最后,我们构建客户端,它会连接到服务器,并调用
Multiply
client/main.go
package main
import (
"context"
"log"
"net/rpc"
"time"
"your_module_path/shared" // 替换为你的模块路径
)
func main() {
client, err := rpc.Dial("tcp", "localhost:1234")
if err != nil {
log.Fatalf("Error dialing RPC server: %v", err)
}
defer client.Close()
args := shared.Args{A: 7, B: 8}
var reply int
// 创建一个带超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
// 异步调用RPC,并在select中处理结果或超时
call := client.Go("Arith.Multiply", args, &reply, nil)
select {
case <-call.Done:
if call.Error != nil {
log.Fatalf("RPC call error: %v", call.Error)
}
log.Printf("Arith: %d * %d = %d", args.A, args.B, reply)
case <-ctx.Done():
log.Printf("RPC call timed out: %v", ctx.Err())
}
// 再次调用,这次使用同步方式
var reply2 int
args2 := shared.Args{A: 10, B: 5}
err = client.Call("Arith.Multiply", args2, &reply2)
if err != nil {
log.Fatalf("Synchronous RPC call error: %v", err)
}
log.Printf("Arith: %d * %d = %d", args2.A, args2.B, reply2)
}要运行这个示例,你需要将
your_module_path
github.com/yourusername/yourproject
go mod init your_module_path
智慧车行小程序,是一个专门为洗车/4S/车辆维修行业打造的小程序,前后端完整代码包括车行动态,养车常识,保养预约,维修预约,洗车美容预约,汽车检测预约等功能。采用腾讯提供的小程序云开发解决方案,无须服务器和域名预约管理:开始/截止时间/人数均可灵活设置,可以自定义客户预约填写的数据项预约凭证:支持线下到场后校验签到/核销/二维码自助签到等多种方式详尽的预约数据:支持预约名单数据导出Excel,打印
0
这确实是一个老生常谈的问题,但每次我启动新项目,尤其涉及内部服务通信时,都会在脑海里过一遍。对我来说,选择Golang RPC还是RESTful API,很大程度上取决于服务的“受众”和“性能敏感度”。
RESTful API,以其无状态、易于理解和调试的特性,以及广泛的工具支持,无疑是构建公共API或与前端(Web/移动)交互的首选。它基于HTTP协议,请求和响应通常是JSON或XML,人类可读性极强。如果你需要一个对外开放、兼容性好的接口,或者你的服务需要被各种异构系统消费,那么REST几乎是你的不二之选。它的松耦合特性也意味着服务之间可以独立演进,不会因为接口变更而频繁牵连。
然而,当谈到微服务之间的内部通信,特别是对性能、延迟有较高要求,或者服务间数据传输量大时,Golang RPC的优势就显现出来了。
net/rpc
gob
在Golang中定义RPC服务接口,其实比很多人想象的要简单,但也有一些约定俗成的规则需要遵守,否则你的方法就无法被RPC系统识别。我刚开始接触时,也踩过一些小坑,比如忘记将方法首字母大写,或者参数类型不对。
核心规则是:
error
nil
举个例子,我们上面的
Multiply
func (t *Arith) Multiply(args *shared.Args, reply *int) error
t *Arith
args *shared.Args
reply *int
error
服务注册则是通过
rpc.Register(serviceInstance)
rpc.RegisterName(name, serviceInstance)
rpc.Register
Arith
rpc.RegisterName
client.Call("Arith.Multiply", ...)理解这些规则和注册机制,是构建健壮Golang RPC服务的基石。它们确保了RPC框架能够正确地反射出你的方法,并进行参数的序列化与反序列化。
在实际生产环境中,网络通信总是充满了不确定性。错误、超时和连接断开是常态,而不是异常。因此,在Golang RPC中,妥善处理这些问题至关重要,它直接关系到服务的健壮性和用户体验。
错误处理 Golang RPC方法的返回类型是
error
nil
client.Call
call.Error
InvalidArgumentError
// 服务器端
func (t *Arith) Divide(args *shared.Args, reply *shared.Quotient) error {
if args.B == 0 {
return errors.New("divide by zero") // 返回自定义错误
}
// ...
return nil
}
// 客户端
var quo shared.Quotient
err := client.Call("Arith.Divide", args, &quo)
if err != nil {
if err.Error() == "divide by zero" {
log.Println("Error: Cannot divide by zero!")
} else {
log.Printf("RPC call failed: %v", err)
}
}超时机制 超时是防止服务雪崩的关键。如果一个RPC调用长时间没有响应,它可能会阻塞客户端,并耗尽资源。在Golang RPC客户端中,我们可以利用
context
context.WithTimeout
client.Go
select
call.Done
ctx.Done
// 客户端示例(已在解决方案中给出)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
call := client.Go("Arith.Multiply", args, &reply, nil)
select {
case <-call.Done:
// RPC调用完成,处理结果
case <-ctx.Done():
// RPC调用超时
log.Printf("RPC call timed out: %v", ctx.Err())
}这种模式非常强大,它允许你在不阻塞主goroutine的情况下,同时管理RPC调用的完成和超时。
连接管理 RPC客户端与服务器的连接管理也需要细心。
rpc.Dial
rpc.Client
rpc.Client
client.Call
sync.Pool
rpc.Client
以上就是GolangRPC客户端与服务器完整示例的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号