golang-gob與rpc

cloudgeek發表於2018-07-21

  今天和大家聊聊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)
9
10//準備編碼的資料
11type P struct {
12    X, Y, Z int
13    Name    string
14}
15
16//接收解碼結果的結構
17type Q struct {
18    X, Y *int32
19    Name string
20}
21
22func main() {
23    //初始化一個資料
24    data := P{345"CloudGeek"}
25    //編碼後得到buf位元組切片
26    buf := encode(data)
27    //用於接收解碼資料
28    var q *Q
29    //解碼操作
30    q = decode(buf)
31    //"CloudGeek": {3,4}
32    fmt.Printf("%q: {%d,%d} ", q.Name, *q.X, *q.Y)
33
34}
35
36func encode(data interface{}) *bytes.Buffer {
37    //Buffer型別實現了io.Writer介面
38    var buf bytes.Buffer
39    //得到編碼器
40    enc := gob.NewEncoder(&buf)
41    //呼叫編碼器的Encode方法來編碼資料data
42    enc.Encode(data)
43    //編碼後的結果放在buf中
44    return &buf
45}
46
47func decode(data interface{}) *Q {
48    d := data.(io.Reader)
49    //獲取一個解碼器,引數需要實現io.Reader介面
50    dec := gob.NewDecoder(d)
51    var q Q
52    //呼叫解碼器的Decode方法將資料解碼,用Q型別的q來接收
53    dec.Decode(&q)
54    return &q
55}

  例2:資料結構到檔案的序列化和反序列化

 1package main
2
3import (
4    "encoding/gob"
5    "os"
6    "fmt"
7)
8
9//試驗用的資料型別
10type Address struct {
11    City    string
12    Country string
13}
14
15//序列化後資料存放的路徑
16var filePath string
17
18func main() {
19    filePath = "./address.gob"
20    encode()
21    pa := decode()
22    fmt.Println(*pa) //{Chengdu China}
23}
24
25//將資料序列號後寫到檔案中
26func encode() {
27    pa := &Address{"Chengdu""China"}
28    //開啟檔案,不存在的時候新建
29    file, _ := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0666)
30    defer file.Close()
31
32    //encode後寫到這個檔案中
33    enc := gob.NewEncoder(file)
34    enc.Encode(pa)
35}
36
37//從檔案中讀取資料並反序列化
38func decode() *Address {
39    file, _ := os.Open(filePath)
40    defer file.Close()
41
42    var pa Address
43    //decode操作
44    dec := gob.NewDecoder(file)
45    dec.Decode(&pa)
46    return &pa
47}

  上面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 int
11}
12
13//定義一個算術型別,其實就是int
14type Arith int
15
16//實現乘法的方法繫結到Arith型別,先不管為什麼是這樣的形式
17func (t *Arith) Multiply(args *Args, reply *int) error {
18    *reply = args.A * args.B
19    return nil
20}
21
22func 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 int
10}
11
12func main() {
13    //連線伺服器端,建立一個client
14    client, _ := rpc.DialHTTP("tcp""127.0.0.1:1234")
15    args := &Args{78}
16    var reply int
17    //通過Call方法呼叫Arith型別的Multiply方法,注意形參
18    client.Call("Arith.Multiply", args, &reply)
19    //得到呼叫結果,輸出Arith: 7*8=56
20    fmt.Printf("Arith: %d*%d=%d ", 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/


相關文章