Go 语言标准库 net/rpc 框架实战:客户端与服务端开发指南

花开有时
花开有时
发布时间:2026-10-15| 2人浏览过
原创

本文以 Go 语言标准库 net/rpc 为基础,详细演示如何构建一个支持 HTTP 协议的远程调用系统。通过模拟书籍阅读管理场景,文章完整梳理了从服务端方法封装、参数结构体定义、连接监听,到客户端实例化、方法调用及日志打印的全部流程,为开发者提供可参考的 RPC 基础架构实现。

核心需求与数据结构定义

为了实现远程调用,首先需要在服务器端公开一组业务方法。本例的需求是允许用户添加书籍信息、设置阅读进度并查询已读页数。为此,我们定义了以下核心结构体:

第 1 步:定义数据模型

目的: 定义用于存储和传输的书籍结构体(Book)及管理阅读状态的容器(ReadingList)。这些类型将作为 RPC 方法入参或返回值的载体。

入口: 新建 Go 文件进行类型定义。

动作: 创建 Book 结构体包含 ISBN、标题等字段;创建 ReadingList 使用切片模拟内存数据库。

代码示例:

// Book represents a book entry
type Book struct {
	ISBN string
	Title, Author string
	Year, Pages int
}
// ReadingList keeps tracks of books and pages read
type ReadingList struct {
	Books []Book
	Progress []int
}

第 2 步:编写辅助与业务方法

目的: 在 ReadingList 上定义具体的增删改查逻辑,包括添加书籍、获取和设置进度。

动作: 实现内部查找索引的助手方法 bookIndex,以及对外公开的业务逻辑。注意处理错误情况(如 ISBN 为空、书籍重复或缺失)。

重要参数: 确保方法签名中第二个参数为指针类型,以便能够修改调用方传入的值。

// AddBook checks if the book is not present and adds it
func (r *ReadingList) AddBook(b Book) error {
	if b.ISBN == "" {
		return ErrISBN
	}
	if r.bookIndex(b.ISBN) != -1 {
		return ErrDuplicate
	}
	r.Books = append(r.Books, b)
	r.Progress = append(r.Progress, 0)
	return nil
}

// GetProgress returns the progress of a book
func (r *ReadingList) GetProgress(isbn string) (int, error) {
	if isbn == "" {
		return -1, ErrISBN
	}
	i := r.bookIndex(isbn)
	if i == -1 {
		return -1, ErrMissing
	}
	return r.Progress[i], nil
}

// SetProgress sets the reading progress
func (r *ReadingList) SetProgress(isbn string, pages int) error {
	if isbn == "" {
		return ErrISBN
	}
	i := r.bookIndex(isbn)
	if i == -1 {
		return ErrMissing
	}
	if p := r.Books[i].Pages; pages > p {
		pages = p // 限制最大页数为书籍总页数
	}
	r.Progress[i] = pages
	return nil
}

构建 RPC 服务端

Go 的 rpc 包对公开方法有严格的要求:方法必须是导出的(首字母大写),接收者必须是引用类型,且必须包含两个参数和一个返回值(error)。由于 RPC 调用只能传递单个非参数类型的对象给函数,当需要多个输入时,需将其打包进一个结构体中。

第 3 步:包装 RPC 方法

目的: 将上一步的业务方法转换为符合 net/rpc 规范的 RPC 方法。这涉及处理返回值的填充(通过指针)以及错误的统一返回。

动作:

  1. 定义通用的错误返回辅助函数 setSuccess,用于根据业务结果更新输出参数的布尔值。
  2. 为 AddBook 和 GetProgress 创建包装器,保持原有业务逻辑不变,但调整签名。
  3. 对于 SetProgress,由于有两个输入参数(ISBN 和页数),需创建中间结构体 Progress 来封装它们。

代码示例:

// 自定义错误定义
var (
    ErrISBN = fmt.Errorf("missing ISBN")
    ErrDuplicate = fmt.Errorf("duplicate book")
    ErrMissing = fmt.Errorf("missing book")
)

// 辅助函数:根据错误设置 success 状态
func setSuccess(err error, b *bool) error {
	*b = err == nil
	return err
}

// RPC 服务的 Receiver 结构体
func (r *ReadingService) AddBook(b Book, success *bool) error {
	return setSuccess(r.ReadingList.AddBook(b), success)
}

func (r *ReadingService) GetProgress(isbn string, pages *int) (err error) {
	*pages, err = r.ReadingList.GetProgress(isbn)
	return err
}

type Progress struct {
	ISBN  string
	Pages int
}

func (r *ReadingService) SetProgress(p Progress, success *bool) error {
	return setSuccess(r.ReadingList.SetProgress(p.ISBN, p.Pages), success)
}

第 4 步:注册服务并启动 HTTP 监听

目的: 初始化 net/http 服务器,监听特定端口以接受外部 RPC 请求。

动作:

  1. 使用 rpc.Register 注册全局 RPC 服务实例。
  2. 使用 rpc.HandleHTTP() 安装默认的路由处理器。
  3. 监听 TCP 地址,例如 :8900,并使用 http.Serve 启动服务。

完成状态判断: 控制台打印 "Server Started" 表示服务已成功运行。

func main() {
	if err := rpc.Register(&book.ReadingService{}); err != nil {
		log.Fatalln(err)
	}
	rpc.HandleHTTP()
	l, err := net.Listen("tcp", ":8900")
	if err != nil {
		log.Fatalln(err)
	}
	log.Println("Server Started")
	if err := http.Serve(l, nil); err != nil {
		log.Fatal(err)
	}
}

实现 RPC 客户端

为了验证服务端的功能,我们需要编写一个客户端程序。客户端也必须拥有与服务端相同的结构体定义(Book, Progress),以便序列化/反序列化数据。

第 5 步:建立连接与发起调用

目的: 连接到服务端并循环测试所有定义的远程方法。

入口: 新的 main 函数。

动作:

  1. 连接: 使用 rpc.DialHTTP("tcp", ":8900") 建立连接,并确保最后关闭客户端。
  2. 通用调用封装: 定义 callClient 函数,自动处理不同返回值类型(如 *bool, *int)的解引用和日志打印,提高测试效率。
  3. 执行测试: 遍历预设的书籍列表,依次调用 AddBook、SetProgress 和 GetProgress。

代码示例:

// 共享的结构体定义
package main
import (
	log "log"
	"net/rpc"
)

type Book struct {
	ISBN          string
	Title, Author string
	Year, Pages   int
}
type Progress struct {
	ISBN  string
	Pages int
}

const hp = "H.P. Lovecraft"
var books = []Book{
	{ISBN: "1540335534", Author: hp, Title: "The Call of Cthulhu", Pages: 36},
	{ISBN: "1980722803", Author: hp, Title: "The Dunwich Horror ", Pages: 53},
	{ISBN: "197620299X", Author: hp, Title: "The Shadow Over Innsmouth", Pages: 40},
	{ISBN: "1540335536", Author: hp, Title: "The Case of Charles Dexter Ward", Pages: 176},
}

func callClient(client *rpc.Client, method string, in, out interface{}) {
	var r interface{}
	if err := client.Call(method, in, out); err != nil {
		out = err
	}
	switch v := out.(type) {
	case error:
		r = v
	case *int:
		r = *v
	case *bool:
		r = *v
	}
	log.Printf("%s: [%+v] -> %+v", method, in, r)
}

func main() {
	client, err := rpc.DialHTTP("tcp", ":8900")
	if err != nil {
		log.Fatalln(err)
	}
	defer client.Close()
	for i, book := range books {
		// 添加书籍
		callClient(client, "ReadingService.AddBook", book, new(bool))
		// 设置进度
		callClient(client, "ReadingService.SetProgress", Progress{
			ISBN:  book.ISBN,
			Pages: 10 + i*i,
		}, new(bool))
		// 获取进度
		callClient(client, "ReadingService.GetProgress", book.ISBN, new(int))
	}
}

以上代码完整实现了 Go 标准库 net/rpc 的基础通信流程。虽然在现代微服务架构中 gRPC 更为流行,但理解 net/rpc 有助于深入掌握 Go 语言的接口设计与反射机制。以上就是 Go 语言 RPC 框架具体使用的详细内容,更多关于 Go 远程调用的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。