Go 使用标准库 net/rpc 包
01
RPC 是什么?
RPC 是远程过程调用(Remote Procedure Call),用于调用方和被调用方两个进程间的交互,并且提供类似本地方法调用的形式。RPC 广泛用于在分布式系统中不同节点间的通信。
02
Go 语言 RPC 标准库
在 Go 语言的标准库中,也提供了一个简单的 RPC 实现(net/rpc)。rpc 包提供对对象在网络或其他 I/O 连接中导出方法的访问。服务器端注册对象,使其作为可见服务,服务的名称是对象类型名称。注册后,对象的导出方法将可远程访问。服务器可以注册不同类型的多个对象(服务),但注册同一类型的多个对象是错误的。
对象的导出方法有以下几点要求:
- 方法的类型是可导出的。
- 方法是可导出的。
- 方法有两个参数,都是可导出类型或内置类型。
- 方法的第二个参数是指针。
- 方法返回一个错误类型。
实际上,方法看起来像这样:
func (t *T) MethodName(argType T1, replyType *T2) error
其中 T1 和 T2 可以通过 encoding/gob 编码进行序列化。即使使用不同的编码解码器,这些限制也适用。将来,对自定义的编码解码器的限制可能会宽松一些。
该方法的第一个参数表示调用方提供的参数;第二个参数表示要返回给调用方的结果参数。方法的返回值(如果不是 nil)作为字符串传递回来,客户端认为该字符串就像由 errors.New 创建的错误一样。如果返回错误,则不会将回复参数发送回客户端。
服务器端可以调用 ServeConn 处理单个连接上的请求。更典型的是,它将创建一个网络监听器并调用 Accept,或者,对于 HTTP 监听器,调用 HandleHTTP 和 http.Serve。
想要使用该服务的客户端会建立连接,然后在连接上调用 NewClient。
更方便的函数是 Dial (DialHTTP) ,会在原始网络连接(HTTP 连接)依次执行这两个步骤。生成的 Client 对象有两个方法,即 Call 和 Go,它们的参数是要调用的服务和方法,一个包含参数的指针,一个用于接收结果的指针。
Call 方法等待远程调用完成。Go 方法异步发送调用请求,并使用返回的 Call结构体类型的 "Done 通道" 传递完成的信号。
除非显式设置了编码解码器,否则 net/rpc 包默认采用 encoding/gob 包编码解码数据。
03
RPC 怎么使用?
通过一个简单的示例,我们演示 Go 语言标准库 net/rpc 的使用方法。
RPC 方法:
服务器端定义一个可导出的 User 类型和一个符合 RPC 方法定义要求的 GetUser 方法:
type User struct {
ID int
Name string
}
// rpc 方法
func (u *User) GetUser(id int, user *User) error {
userMap := map[int]User{
1: {ID: 1, Name: "frank"},
2: {ID: 2, Name: "lucy"},
}
if userInfo, ok := userMap[id]; ok {
*user = userInfo
}
return nil
}
服务器端:
服务器端被调用(用于 HTTP 服务):
func main() {
_ = rpc.Register(new(message.User))
rpc.HandleHTTP()
listener, _ := net.Listen("tcp", ":8081")
_ = http.Serve(listener, nil)
}
客户端:
此时,客户端可以看到具有 "User.GetUser" 方法的服务 "User"。要调用方法,客户端首先呼叫服务器端:
client, _ := rpc.DialHTTP("tcp", ":8081")
然后客户端可以进行远程调用:
Call 方法,同步调用:
id := 1
var user message.User
_ = client.Call("User.GetUser", id, &user)
fmt.Println(user)
Go 方法,异步调用:
userCall := client.Go("User.GetUser", id, &user, nil)
if replyCall := <-userCall.Done; replyCall != nil {
fmt.Println(user)
}
服务器端的实现通常为客户端提供简单、类型安全的包装。
net/rpc 包已冻结,不接受新功能。
04
总结
本文简要描述 Go 语言标准库 net/rpc 包的使用方法,通过阅读本文,读者应该已经对Go 语言标准库 net/rpc 有了初步的认识。
- mysql的查询、子查询及连接查询
- 简陋的分布式爬虫(附项目代码地址)
- 使用PowerShell简化我的工作
- 几个提高工作效率的Python内置小工具
- J2EE相关总结
- (53) 剖析Collections - 算法 / 计算机程序的思维逻辑
- Flask使用Blueprint进行多模块应用的编写
- 优雅的在终端中编写Python
- Eclipse相关问题
- (54) 剖析Collections - 设计模式 / 计算机程序的思维逻辑
- Django 博客教程(三):创建应用和编写数据库模型
- package-info.java文件详解
- 在Spring下集成ActiveMQ
- Java中只有按值传递,没有按引用传递!
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 设计模式 | 适配器模式
- 设计模式 | 组合模式
- 设计模式 | 观察者模式
- 设计模式 | 原型模式
- CORS跨域资源共享及解决方案
- Vue 父子组件通信 兄弟组件通信 深层组件通信 方式一览
- Vue使用Element实现增删改查+打包
- 状态管理之Vuex (二) 异步管理
- Spring中AOP相关的API及源码解析,原来AOP是这样子的
- 状态管理之Vuex (一) 基操勿六
- 形式化分析工具(五)使用CAS +语法轻松编写HLPSL规范
- 你知道Spring是怎么将AOP应用到Bean的生命周期中的吗?
- 太实用了!自己动手写软件——密码验证器的界面实现
- 【TBase开源版测评】深度测评TBase的shard分片和冷热分离存储特性
- Python爬虫练手,一个简单的Python资讯采集案例