今天和大家聊聊golang中怎么使用rpc,rpc数据传输会涉及到gob编码,所以先讲讲gob,别担心,就算你完全没有接触过gob与rpc,只要知道rpc的中文是远程过程调用,剩下的我都能给你讲明白(带你入门不包你精通)!
一、数据结构编码之gob
gob全称为:Go binary
Golang自带的一个数据结构序列化编码/解码工具,也就是说gob可以讲go中的一个数据结构序列化成某种东西,还能反序列化!序列化成啥我们后面来看,不管是变成一个字符串,变成二进制流,变成啥先不管,反正作用就是序列化。
Gob使用时我们需要关注Encoder和Decoder对象,顾名思义,一个是编码的时候用的,一个是解码的时候用的,我们看一下怎么获取这两个对象先:
所以很明确,需要调用这两个函数来获取Encoder和Decoder对象。注意这里的参数是io.Writer和io.Reader接口类型,我们在上一讲介绍过这两个接口,所以这里需要的参数分别是实现了io.Writer和io.Reader接口类型的对象即可。
Encoder和Decoder分别有一个主要的方法是:
看到这里我们已经可以得到如下结论:
Gob 使用 io.Writer 接口,通过 NewEncoder() 函数创建 Encoder 对象通过调用 Encode()方法实现编码操作;使用 io.Reader 接口,通过 NewDecoder() 函数创建 Decoder 对象并调用 Decode()方法完成解码操作!
接下来我们试着用一下这个Encoder和Decoder,就轻轻松松入门gob了,来看第一个例子
例1:数据结构与bytes.Buffer之间的转换(编码成字节切片)
1package main 2 3import ( 4 "bytes" 5 "fmt" 6 "encoding/gob" 7 "io" 8) 910//准备编码的数据11type P struct {12 X, Y, Z int13 Name string14}1516//接收解码结果的结构17type Q struct {18 X, Y *int3219 Name string20}2122func main() {23 //初始化一个数据24 data := P{3, 4, 5, "CloudGeek"}25 //编码后得到buf字节切片26 buf := encode(data)27 //用于接收解码数据28 var q *Q29 //解码操作30 q = decode(buf)31 //"CloudGeek": {3,4}32 fmt.Printf("%q: {%d,%d}\n", q.Name, *q.X, *q.Y)3334}3536func encode(data interface{}) *bytes.Buffer {37 //Buffer类型实现了io.Writer接口38 var buf bytes.Buffer39 //得到编码器40 enc := gob.NewEncoder(&buf)41 //调用编码器的Encode方法来编码数据data42 enc.Encode(data)43 //编码后的结果放在buf中44 return &buf45}4647func decode(data interface{}) *Q {48 d := data.(io.Reader)49 //获取一个解码器,参数需要实现io.Reader接口50 dec := gob.NewDecoder(d)51 var q Q52 //调用解码器的Decode方法将数据解码,用Q类型的q来接收53 dec.Decode(&q)54 return &q55}
例2:数据结构到文件的序列化和反序列化
1package main 2 3import ( 4 "encoding/gob" 5 "os" 6 "fmt" 7) 8 9//试验用的数据类型10type Address struct {11 City string12 Country string13}1415//序列化后数据存放的路径16var filePath string1718func main() {19 filePath = "./address.gob"20 encode()21 pa := decode()22 fmt.Println(*pa) //{Chengdu China}23}2425//将数据序列号后写到文件中26func encode() {27 pa := &Address{"Chengdu", "China"}28 //打开文件,不存在的时候新建29 file, _ := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0666)30 defer file.Close()3132 //encode后写到这个文件中33 enc := gob.NewEncoder(file)34 enc.Encode(pa)35}3637//从文件中读取数据并反序列化38func decode() *Address {39 file, _ := os.Open(filePath)40 defer file.Close()4142 var pa Address43 //decode操作44 dec := gob.NewDecoder(file)45 dec.Decode(&pa)46 return &pa47}
上面2个例子都不难,我去掉了错误处理之类的代码,尽量注释了每块代码,耐心看完这2个例子应该就能体会gob的encode和decode精髓了。
理解gob是什么的基础上,如果你需要使用gob开发,建议详细看一下官方文档,了解一下更多的细节:https://golang.org/pkg/encoding/gob/
二、golang中的rpc入门
如果你之前没有做过基于rpc通信的开发工作,直接去网上查rpc相关的知识点的时候很可能会一脸蒙圈,rest api咋就那么好理解,一个http请求过去就行了,rpc咋个回事,看不懂呀。。。
所以我不会和多数教程一样为了追求详细或者展示自己技术多牛而去写很长的例子,扯一堆专业的概念,我们先最快的方式体验一下rpc调用的感觉!
rpc服务端
1package main 2 3import ( 4 "net" 5 "net/rpc" 6 "net/http" 7) 8 9type Args struct {10 A, B int11}1213//定义一个算术类型,其实就是int14type Arith int1516//实现乘法的方法绑定到Arith类型,先不管为什么是这样的形式17func (t *Arith) Multiply(args *Args, reply *int) error {18 *reply = args.A * args.B19 return nil20}2122func main() {23 //得到一个Arith类型的指针实例24 arith := new(Arith)25 //注册到rpc服务26 rpc.Register(arith)27 //挂到http服务上28 rpc.HandleHTTP()29 //开始监听30 l, _ := net.Listen("tcp", ":1234")31 http.Serve(l, nil)32}
rpc客户端
1package main 2 3import ( 4 "net/rpc" 5 "fmt" 6) 7 8type Args struct { 9 A, B int10}1112func main() {13 //连接服务器端,创建一个client14 client, _ := rpc.DialHTTP("tcp", "127.0.0.1:1234")15 args := &Args{7, 8}16 var reply int17 //通过Call方法调用Arith类型的Multiply方法,注意形参18 client.Call("Arith.Multiply", args, &reply)19 //得到调用结果,输出Arith: 7*8=5620 fmt.Printf("Arith: %d*%d=%d\n", args.A, args.B, reply)21}
上面2段程序很简短,可能你现在还不能理解其中的细节,但也请耐心看完,这个时候你应该能够心里有个rpc调用的概念了,客户端直接调用了服务器端的一个函数传递过去参数列表和接收返回值的对象,获得调用结果。
三、rpc的一些细节
下面我们再来看一些rpc相关的细节
首先能够被rpc调用的方法应该看起来像这样:
func (t *T) MethodName(argType T1, replyType *T2) error
大概解释一下:
- 函数必须是可导出的(首字母大写)
- 必须有两个导出类型的参数,第一个参数用来接收参数,第二个参数是返回给客户端的结果参数,第二个参数必须是指针类型的
- 函数还要有一个返回值error
- T1、T2能够被encoding/gob编码
看到这里你应该对于rpc的作用有了一定的认识,go中rpc包的用法简单来看就是准备一个类型,绑定一堆符合规范的方法,然后注册给rpc服务,监听客户端连接,客户端通过rpc包提供的Call方法可以调用到server注册好的方法。更多细节可以看一下官方文档:https://golang.org/pkg/net/rpc/
原文地址:https://www.cnblogs.com/cloudgeek/p/9348267.html